int __cdecl main(int argc, constchar **argv, constchar **envp) { int v4; // [rsp+Ch] [rbp-4h] BYREF
setvbuf(stdout, 0LL, 2, 0LL); setvbuf(stdin, 0LL, 1, 0LL); fill_secret_buffer(); write(2, "Welcome to my game, choose any number between 1-10\n", 0x33uLL); write(2, "Enter a number: ", 0x10uLL); __isoc99_scanf("%d", &v4); getchar(); if ( v4 > 10 || v4 <= 0 ) { write(2, "Wrong number entered\n", 0x15uLL); exit(0); } if ( (v4 & 1) != 0 ) odd_option(); else even_option(); // not vuln write(2, "Bye bye\n", 8uLL); return0; }
main 함수에서 수 하나를 입력받는데 0부터 9사이의 범위만 인정한다. 그 다음 & 연산을 진행하는데 입력 값이 홀수라면 odd_option() 함수를 실행하고 아니라면 even_option() 함수를 실행한다. 후자의 함수에 취약점이 없기 때문에 간단하게 먼저 살펴보자.
write(2, "Enter your name: ", 0x11uLL); fgets(s, 100, stdin); s[strcspn(s, "\r\n")] = 0; v2 = sprintf(temp_buffer, "Hello %s, we are sorry but you gave us the wrong input. Please try again.\n", s); return write(2, temp_buffer, v2); }
지역변수 s에 이름을 입력받는다. bof가 일어날 가능성을 염두에 뒀지만 108 크기에 100만큼 입력하기 때문에 bof는 일어나지 않는다. 이 함수에서 취약점은 발생하지 않는다.
write(2, "Let's begin, but first here is how you play:\n", 0x2DuLL); write(2, "- A number will be shown to you\n", 0x20uLL); write(2, "- You have to enter two strings\n", 0x20uLL); write(2, "- - The first should be your name\n", 0x22uLL); write(2, "- - The second string should be equal to the secret code\n", 0x39uLL); write(2, "- If you successfully guess the secret code, you win!\n", 0x36uLL); v3 = sprintf(temp_buffer, "\nHere's the number: %lld\n", s); write(2, temp_buffer, v3); write(2, "Enter first input please: ", 0x1AuLL); fgets(s, 200, stdin); write(2, "Enter second input please: ", 0x1BuLL); fgets(dest, 130, stdin); // bof result = strcpy(dest, secret_buffer); if ( !result ) { write(2, "Congrats! You win!\n", 0x13uLL); exit(0); } return result; }
208 크기의 지역변수 s에 200만큼 입력하고 108 크기의 지역변수 dest에 130만큼 입력한다. 여기서 bof가 발생한다. 바로 뒤에 srctpy함수에서 secret_buffer를 dest에 복사한다. 이 함수가 실행되기 전에 main에서 fill_secret_buffer() 함수를 호출하는데 여기서 secret_buffer 전역변수를 채운다.
결론적으로 알아낸 것은 지역변수 dest에서 130-0x70 즉, 18만큼 overflow가 난다는 것이다.
Vulnerability
overflow가 18만큼난다? sfp(rbp), ret..
ret까지 안전하게 덮고 2바이트 더 overflow가 난다. ROP를 하기에는 overflow가 충분히 나지 않는다. 이럴 때 해야하는 것이 Stack pivoting이다. 이와 관련해서는 (구 블로그에도 써놨지만) 시간이 된다면 이 블로그에 글을 새로 쓸 예정이다.
Exploit
overflow가 ret까지 날 때는 stack pivoting 기법을 고려해볼 수 있겠다. 이를 위해서 fake stack을 구성할 공간이 필요하다. 이 문제에서는 “Enter first input please: “에서 지역변수 s에 200만큼 입력할 수 있다. 200바이트면 ROP 페이로드를 적기에 충분하다. 여기를 fake stack으로 한다! 땅땅
Exploit Scenario
PART1
libc leak하는 ROP 페이로드를 작성하여 stack에 구성
위 stack으로 흐름 변경
PART2
system('/bin/sh\x00')을 호출하는 ROP 페이로드를 작성하여 stack에 구성
write(2, "Oooh! You reached the hidden level, type the mantra to unlock the hidden door:\n", 0x4FuLL); result = fgets(s, 512, stdin); if ( a1 != -559038737 ) { write(2, "Did you cheat?\n", 0xFuLL); exit(0); } return result; }
2.
늦장 부리다가 롸업을 너무 늦게 작성했다. 이 씨텝… 서버 오래 살아있어서 좋았는데 롸업 작성할 당시 서버가 닫혔다. 롸업은.. 제때제때 빨리빨리 작성하자 T_T.