初赛
初赛在杭州,地点在中国移动杭州研发中心。
顺便去旁边的杭州师范大学(仓前新校区)逛了下,真羡慕别人的学校:
Crypto
垃圾比赛,才2道Crypto,就值个30分。
Crypto1 10pt
打开题目描述,看到是一段字符串,直接看到结尾,c666
,内心甚至毫无波动,。
|
|
Crypto2 20pt
RSA.txt
n= 703739435902178622788120837062252491867056043804038443493374414926110815100242619
e= 59159
c= 449590107303744450592771521828486744432324538211104865947743276969382998354463377
m=???
n.bit_length()
看了一下,才269bit,直接factor。
可惜没外网,用不了小网站,不过还好有sage,放后台几分钟就跑出来了。
|
|
exp.py
|
|
抢了2个一血,题目太简单了。。
Steg
Steg1 xxpt
拿到一个图片,用010
打开,能看到output
里面报错,说CRC Mismatch @ chunk[0]
,再去看看原图,发现图片的高度是有点不太对。
利用CRC32
来计算正确的宽度和高度。
|
|
修改对应位置的高度,然后保存。
本以为flag就会在图片的下面的,可是并没有。。
试着粗略地看了下图片的lsb
,没有发现什么有用的信息。
后来队友说,用Stegsolver
在某个通道里发现了一个二维码。好吧,毕竟垃圾比赛,题目也就只能是这些套路了。
不能上外网,手机也都上交了,还好之前有了解过一个命令行工具zbarimg
可以离线解析二维码。把图片保存下来,然后zbarimg xxx.png
就能得到flag
。
steg2 xxpt
拿到一个.exe
文件,没敢运行,直接先010
看一下,发现是个图片的base64
。
复制进浏览器url
栏,回车得到一张刘大爷的图片:
再拖进010
,发现末尾藏了一个压缩包。
复制hex
数据,新建文件,Edit As: Hex
,粘贴,保存,得到压缩包。
打开压缩包,发现有密码。试了下伪加密,并不是。
看来要爆破密码,不过这时习惯性地看了下前面那张图片的details
。
嗯,都是套路。
password is QwE12#
,输入解压得到flag{06e9e74c449042d19e6ee3f6c04fed92}
。
Misc
Misc3 15pt
拿到一个.raw
文件,看起来是个取证题,不过没管那么多,还是拖进010
。
搜索字符串flag{
,直接看到
试着交了下,不对。
hex
转成ascii
,提交,正确!
这题目真垃圾。。。
Misc1 30pt
没外网,不太会流量包分析的题目。
(赛后复现)
流量分析题,wifi
流量,wep
加密,告诉我们密码是6666xxxx
。
掩码攻击,上网搜寻了一番,找了一个linux
环境下的工具aircrack-ng
。
不太会写掩码爆破bb的命令行参数,直接先生成了一个字典,然后字典攻击。
|
|
所以flag
应该就是flag{0566668912-f059-448f}
。
Misc2 30pt
(赛后复现)
题目要求从流量包里面找到黑客用的菜刀密码。
里面有讲到:
同样试着先http.request.method == POST
过滤。
按上面那题的思路,菜刀密码应该就是cmd
,可是这一题跟上面那一题有一个区别就是这边多了一个strrev
。
比赛的时候,我交过flag{cmd}
,并不对。
所以flag
到底是什么??
…
Reverse
Re2 30pt
一道迷宫题,输入key
,走出去就输出Good!
,走错了就error!
,flag是key
的md5值。
要过两道check,两道check基本上是一样的,唯一的区别就是check1要保证18步走出去,而check2则没有步数限制。
迷宫如下:
201111111100000000000000000011111
101111111101111111111011110011111
101111111101111111111011110011111
100000000000000500000100000000011
111111110101111011111111111111111
111100001101111000000000000000000
111100101000000111111111111111112
5是出发点,只能走0,要走到2。
'w': 上
'.': 下
'0': 左
'm': 右
这题就很沙雕,没说必须两次要走不同的出口,导致有多解。
check1限制了18步,那只能左上出去。
14左+3上+1左:'0'*14 + 'w'*3 + '0' == '00000000000000www0'
一开始,我觉得check2直接按照check1的走法完全没问题。
也就是:00000000000000www000000000000000www0
交flag,显示不对。。
找来主办方,主办方说这题是有点坑,然后又提示我后半部分有问题。
嗯,那看来check2只能从右下出去了。
2下+17右+1下: '.'*2 + 'm'*17 + '.' == ..mmmmmmmmmmmmmmmmm.
那么key = 00000000000000www0..mmmmmmmmmmmmmmmmm.
转成md5
|
|
交了flag
,终于对了。。又拿了个一血,不过并没有什么加成。。
Re1 30pt
先要脱个upx的壳。
没怎么打过逆向,不会脱壳,又没外网,现场学都学不了。。 tcl…
(赛后复现)
upx壳,原理先暂时缓一缓,找个工具脱了再说。
上网搜寻了一波,找到了一个UPX Easy GUI
,直接脱。
脱完就很简单了。
输入长度不超过27,过一个check。
input经过一通替换后变为tfoQ5ckkwhX51HYpxAjkMQYTAp5
。
- diff = input[i] - key[i]
- output[i] = table[diff % 4][diff]
大致流程可以看[源代码]。(https://www.cnblogs.com/QKSword/p/9095242.html )
exp.py
|
|
md,又是一道多解。
flag{this_is_a_easy_suanfa}
PWN
三道堆题,dnm
Web
队友说三道题全都是第五空间的原题。。
垃圾比赛。
下午AWD
完全不会。。
手动尝试用初始密码登录别的靶机,找到了3台。。迅速改了密码。
然后每回合手动交flag。。
Summary
Web相关的等有时间要去学一手了。
AWD也要好好搞一下了。
决赛
决赛在西安锦江国际酒店,五星级。
不怎么会拍照:
Introduction
全程断网比赛,还好电脑里存了很多东西,现场自学。。。
第一天早上个人赛
本来是第一的,而且跟第二分差拉的挺大的。奈何比赛时间有点长,到了后面,下面的人都赶上来了。。
Industrial_01
用wireshark
打开流量包,ip.dst == 10.1.1.49
过滤流量包。
右键->追踪TCP流->发现00411000002018008008
为PLC的序列号。
hashlib.md5(b'00411000002018008008').hexdigest()
获得flag。
flag{57ab8a9c2f5962abf9d27a26343e04af}
crackme01
IDA打开
F5反编译,发现几个if
后,会输出正确的flag。
encryptCTF{gdb_or_r2?}
二维码签到
linux下,zbarimg qr.png
获得flag。
flag{have_a_good_luck}
简单的RSA
sagemath直接分解n
|
|
memory
|
|
指定系统类型并查看进程表
|
|
发现有一个notepad进程
|
|
小明的键盘
键盘流量包分析
tshark -r usb.pcapng -T fields -e usb.capdata
导出键位信息
0000090000000000
0000000000000000
00000f0000000000
0000000000000000
0000040000000000
0000000000000000
00000a0000000000
0000000000000000
0200000000000000
02002f0000000000
0200000000000000
0000000000000000
0000220000000000
0000000000000000
0000060000000000
0000000000000000
0000220000000000
0000000000000000
00001f0000000000
0000000000000000
0000040000000000
0000000000000000
0000070000000000
0000000000000000
0000060000000000
0000000000000000
0000070000000000
0000000000000000
00001f0000000000
0000000000000000
0000050000000000
0000000000000000
0000060000000000
0000000000000000
0000060000000000
0000000000000000
0000270000000000
0000000000000000
0000270000000000
0000000000000000
0000270000000000
0000000000000000
0000040000000000
0000000000000000
0000250000000000
0000000000000000
0000050000000000
0000000000000000
0000210000000000
0000000000000000
0000050000000000
0000000000000000
0000070000000000
0000000000000000
0000220000000000
0000000000000000
0000080000000000
0000000000000000
0000050000000000
0000000000000000
0000200000000000
0000000000000000
0000230000000000
0000000000000000
0000050000000000
0000000000000000
0000250000000000
0000000000000000
0000210000000000
0000000000000000
0000060000000000
0000000000000000
0000070000000000
0000000000000000
0000080000000000
0000000000000000
0200000000000000
0200300000000000
0200000000000000
0000000000000000
根据电脑里存了的Universal Serial Bus(USB) manual.pdf
里,关于Keyboard的对照表,转成对应的字符。
flag{5c52adcd2bcc000a8b4bd5eb36b84cde}
VCSA
文件内容实际上一个base64编码的图片,复制进浏览器url栏,回车,拿到图片。
是一个jpg
文件,图片元数据里有QwE12#
。
用010 Editor
打开,发现末尾有一个压缩包。
修改后缀为.zip
,打开,需要密码,输入QwE12#
正确,解压得到flag。
flag{06e9e74c449042d19e6ee3f6c04fed92}
五彩斑斓的二维码
将彩色像素值转成白色或者黑色
|
|
得到64张二维码。
zbarimg *.png
获得3段flag
QR-Code:flag{5bfc2c45
QR-Code:6d10a8b830a6f
QR-Code:ed7cfdf08f3}
flag{5bfc2c456d10a8b830a6fed7cfdf08f3}
第一天下午团队赛
3号机
在Lib\Config\Controllers.php
下发现有后门
构造payload
,写脚本全场打:
|
|
打到后面,就只有1-2台能打了。。
2号机
没审出洞,还一直被打,修不了。
到比赛结束前半个小时,才发现原来主办方提供了每一轮的流量。。。。
然后开始修洞,到比赛结束都还没修好。。
1号机
2web+1crypto,没办法,只能我来看看pwn了
程序挺啰嗦的。
逆到一个退出的逻辑:
存在缓冲区溢出。
把read(0, &buf, 0x70uLL)
patch成read(0, &buf, 0x50uLL)
:
顺手还patch了一些别的不太安全的地方。
scp
上传至1号机,成功防住!
TL;DR
防住了1号机和3号机,3号机还能每轮交几个flag。最后排名5/12
,tcl。。。
第二天上午精英个人赛
第一天个人赛拿了个第四。。前5能进这个精英个人赛。
一拿到题,6道题,感觉没有一个能出。
每个题都看了遍,发现就智能合约
那题可以做一下。
智能合约
请根据合约及其交互信息找出其中隐藏的信息。
Bytecode:
0x60806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806304618359146100725780631cbeae5e1461009f578063890eba68146100cc578063a2da82ab146100f7578063f0fdf83414610127575b600080fd5b34801561007e57600080fd5b5061009d60048036038101908080359060200190929190505050610154565b005b3480156100ab57600080fd5b506100ca6004803603810190808035906020019092919050505061015e565b005b3480156100d857600080fd5b506100e1610171565b6040518082815260200191505060405180910390f35b34801561010357600080fd5b50610125600480360381019080803560ff169060200190929190505050610177565b005b34801561013357600080fd5b50610152600480360381019080803590602001909291905050506101bb565b005b8060008190555050565b6000548114151561016e57600080fd5b50565b60005481565b60008060009150600090505b60108110156101ab576008829060020a0291508260ff16821891508080600101915050610183565b8160005418600081905550505050565b8060036000540201600081905550505600a165627a7a7230582012c9c1368a7902a818e339b8db79b7130db8795bd2a793898b509dc020d960d20029
Opcode:
PUSH1 0x80
PUSH1 0x40
MSTORE
PUSH1 0x04
CALLDATASIZE
LT
PUSH2 0x006d
JUMPI
PUSH1 0x00
CALLDATALOAD
PUSH29 0x0100000000000000000000000000000000000000000000000000000000
SWAP1
DIV
PUSH4 0xffffffff
AND
DUP1
PUSH4 0x04618359
EQ
PUSH2 0x0072
JUMPI
DUP1
PUSH4 0x1cbeae5e
EQ
PUSH2 0x009f
JUMPI
DUP1
PUSH4 0x890eba68
EQ
PUSH2 0x00cc
JUMPI
DUP1
PUSH4 0xa2da82ab
EQ
PUSH2 0x00f7
JUMPI
DUP1
PUSH4 0xf0fdf834
EQ
PUSH2 0x0127
JUMPI
JUMPDEST
PUSH1 0x00
DUP1
REVERT
JUMPDEST
CALLVALUE
DUP1
ISZERO
PUSH2 0x007e
JUMPI
PUSH1 0x00
DUP1
REVERT
JUMPDEST
POP
PUSH2 0x009d
PUSH1 0x04
DUP1
CALLDATASIZE
SUB
DUP2
ADD
SWAP1
DUP1
DUP1
CALLDATALOAD
SWAP1
PUSH1 0x20
ADD
SWAP1
SWAP3
SWAP2
SWAP1
POP
POP
POP
PUSH2 0x0154
JUMP
JUMPDEST
STOP
JUMPDEST
CALLVALUE
DUP1
ISZERO
PUSH2 0x00ab
JUMPI
PUSH1 0x00
DUP1
REVERT
JUMPDEST
POP
PUSH2 0x00ca
PUSH1 0x04
DUP1
CALLDATASIZE
SUB
DUP2
ADD
SWAP1
DUP1
DUP1
CALLDATALOAD
SWAP1
PUSH1 0x20
ADD
SWAP1
SWAP3
SWAP2
SWAP1
POP
POP
POP
PUSH2 0x015e
JUMP
JUMPDEST
STOP
JUMPDEST
CALLVALUE
DUP1
ISZERO
PUSH2 0x00d8
JUMPI
PUSH1 0x00
DUP1
REVERT
JUMPDEST
POP
PUSH2 0x00e1
PUSH2 0x0171
JUMP
JUMPDEST
PUSH1 0x40
MLOAD
DUP1
DUP3
DUP2
MSTORE
PUSH1 0x20
ADD
SWAP2
POP
POP
PUSH1 0x40
MLOAD
DUP1
SWAP2
SUB
SWAP1
RETURN
JUMPDEST
CALLVALUE
DUP1
ISZERO
PUSH2 0x0103
JUMPI
PUSH1 0x00
DUP1
REVERT
JUMPDEST
POP
PUSH2 0x0125
PUSH1 0x04
DUP1
CALLDATASIZE
SUB
DUP2
ADD
SWAP1
DUP1
DUP1
CALLDATALOAD
PUSH1 0xff
AND
SWAP1
PUSH1 0x20
ADD
SWAP1
SWAP3
SWAP2
SWAP1
POP
POP
POP
PUSH2 0x0177
JUMP
JUMPDEST
STOP
JUMPDEST
CALLVALUE
DUP1
ISZERO
PUSH2 0x0133
JUMPI
PUSH1 0x00
DUP1
REVERT
JUMPDEST
POP
PUSH2 0x0152
PUSH1 0x04
DUP1
CALLDATASIZE
SUB
DUP2
ADD
SWAP1
DUP1
DUP1
CALLDATALOAD
SWAP1
PUSH1 0x20
ADD
SWAP1
SWAP3
SWAP2
SWAP1
POP
POP
POP
PUSH2 0x01bb
JUMP
JUMPDEST
STOP
JUMPDEST
DUP1
PUSH1 0x00
DUP2
SWAP1
SSTORE
POP
POP
JUMP
JUMPDEST
PUSH1 0x00
SLOAD
DUP2
EQ
ISZERO
ISZERO
PUSH2 0x016e
JUMPI
PUSH1 0x00
DUP1
REVERT
JUMPDEST
POP
JUMP
JUMPDEST
PUSH1 0x00
SLOAD
DUP2
JUMP
JUMPDEST
PUSH1 0x00
DUP1
PUSH1 0x00
SWAP2
POP
PUSH1 0x00
SWAP1
POP
JUMPDEST
PUSH1 0x10
DUP2
LT
ISZERO
PUSH2 0x01ab
JUMPI
PUSH1 0x08
DUP3
SWAP1
PUSH1 0x02
EXP
MUL
SWAP2
POP
DUP3
PUSH1 0xff
AND
DUP3
XOR
SWAP2
POP
DUP1
DUP1
PUSH1 0x01
ADD
SWAP2
POP
POP
PUSH2 0x0183
JUMP
JUMPDEST
DUP2
PUSH1 0x00
SLOAD
XOR
PUSH1 0x00
DUP2
SWAP1
SSTORE
POP
POP
POP
POP
JUMP
JUMPDEST
DUP1
PUSH1 0x03
PUSH1 0x00
SLOAD
MUL
ADD
PUSH1 0x00
DUP2
SWAP1
SSTORE
POP
POP
JUMP
STOP
部分和合约的交互记录
log1:
0xa2da82ab0000000000000000000000000000000000000000000000000000000000000009
log2:
0xf0fdf83400000000000000000000000000000000000000000000000000000000deadbeaf
log3:
0xa2da82ab0000000000000000000000000000000000000000000000000000000000000007
log4:
secret.flag
{
"0": "uint256: 36269314025157789027829875601337027084"
}
没外网,不然在线反编译网站一搞,直接得到伪代码,比看这个字节码舒服多了。
还好之前去打数字经济,逆过伪代码的智能合约,大致了解这个合约代码的一个结构。而且也稍微接触过汇编代码,有一定的理解。
最巧的是,之前有download过Solidity
的官方文档;而且之前也扫过一遍这个文档,有印象在这个文档里面是有Solidity
的底层字节码相关的东西的。
在电脑里,找到了文档:
在文档里,找到了字节码所在的位置:
它居然说,
This document does not want to be a full description of the Ethereum virtual machine, but the following list can be used as a reference of its opcodes.
虽然没给出全面的描述,但还是给出了一些指令的含义,以及KVM
的底层实现。
就一个栈结构,很简单。
先观察了一下每次交互的记录,根据上次数字经济的经验,前面0xa2da82ab
应该就是合约里面的函数所对应的一个Hash值,而末尾的00000009
就应该是传入这个函数的参数。
所以这个交互记录就是:
call了0xa2da82ad
这个函数两次,参数分别是9和7。
call了0xf0fdf834
这个函数一次,参数是0xdeadbeaf
。
所以只要搞清楚这两个函数干了什么就可以了。
开逆!
随便逆了一会,完全分不清这个里面的JUMP
到底跳到哪里去了。
然后发现有一个JUMPDEST
,猜测就是`JUMP的目的地。
搜了一下一共有24个JUMP
和JUMPI
,对应的也正好有24个JUMPDEST
。
每次jump之前都会有一个PUSH2 0x????
操作,翻了下文档,发现这就是jump
的目的地。
但是0x????
没有在代码里面标识啊!
又发现0x????
里面没有重复的,而且有一个大小关系。
猜测,如果把这些0x????
按大小排序,对应的应该就是从上往下的JUMPDEST
的位置。
用python写了个脚本,并手动在每个JUMPDEST
前标上当前的地址。
根据数字经济的经验,合约代码的开头是一个跳转表。
先搞这个0xa2da82ab
,对应的开始位置应该是0x00f7
。
再找到0x177
处的逻辑:
就是一个16轮操作。
最后跳到0x01ab
这里,对storage[0]
异或上对参数进行16轮操作后的值。
再来看看0xf0fdf834
:
函数开头位置在0x0127
。
然后再跳转到0x01bb
,
把storage[0]
乘3,再加上0xdeadbeaf
。
实际上比赛结束前半个小时我才刚块逆完。
主办方看没人做出来,直接扔出了已经反汇编好的伪代码:
|
|
这下就很简单了。
先逆log3
:storage[0]
处直接异或0xa2da82ad(7)
;
再逆log2
:(storage[0] - 0xdeadbeaf) - 3
;
最后逆log1
:storage[0]
处异或0xa2da82ad(9)
。
得到的就是初始的storage[0]
,也就是flag。
python脚本如下:
|
|
拿了全场唯一的一血:
颁奖
因为还要去赶着去湖湘杯,所以打完就溜了,没等到颁奖典礼。
最后拿了团队二等奖+个人赛二等奖+精英赛个人赛二等奖+新锐奖,以及5k奖金。