这回我又被出题人逼成re手了
其他题型彻底翻车,啥也不会
个人成绩:3031
pt/8
kill/8
名/1
一血/1
三血
Misc
拼图v2.0
WriteUp
原图是网页图标favicon.png
。
本题需要旋转拼图块,所以gaps
似乎用不上,听说大多数人都是手拼的,俺也一样
flag
flag{17eb22e6-560f-404c-b008-a30af26cb500}
请问大吉杯的签到是在这里签吗
WriteUp
首先拿到二维码图片1.png
,一扫发现是气人留言,然后发现文件尾还有个zip
,那就解开拿到另一张二维码图片2.png
,还是气人留言,再解压,得到3.png
,再解压得到4.png
,然后就死胡同了。
于是回头看,啥也没发现,第二天水群的时候终于在群大佬的提醒下发现2.png
的最低位存在异常,把最低位的图存出来,然后xor一下,就得到了一张看着就很像猪圈密码的东西。然后解猪圈就能拿到flag。
flag
flag{dajiadoaidjb}
Crypto
RealSimpleAlgorithm
题目
from Crypto.Util.number import *
from secret import flag
def findPrime(k):
return k if isPrime(k) else findPrime(k+1)
p = getPrime(256)
q = findPrime(20210123 * p * p)
r = findPrime(p * q * q)
s = findPrime(p * q * r)
n = p * q * r * s
e = 0x10001
m = bytes_to_long(flag)
w = open('output','wb')
w.write(long_to_bytes(n))
w.write(b'\n\n')
w.write(long_to_bytes(pow(m, e, n)))
WriteUp
这题的话分析一下会发现实际上q
和20210123 * p * p
很接近,r
和p * q * q
很接近,s
和p * q * r
很接近(因为findPrime
函数是++
遍历过去的,下一个质数与原数相比大不了多少,与256bit
这个量级比起来几乎可以忽略不计)
所以我们可以直接列出如下近似等式。
q = 20210123 * p * p
r = p * q * q
s = p * q * r
合并可得到n = p * q * r * s = p * (20210123 * p * p) * (p * (20210123 * p * p) * (20210123 * p * p)) * (p * (20210123 * p * p) * (p * (20210123 * p * p) * (20210123 * p * p))) = (p ** 16) * (20210123 ** 6)
所以直接就能求出p
或者p
的究极近似值,然后代进脚本把需要的参数算出来就能拿到flag。
那么先把long_to_bytes
的n
和c
重新读成long
,然后冲就完事了。
from Crypto.Util.number import *
import gmpy2
def findPrime(k):
return k if isPrime(k) else findPrime(k+1)
data = open("output", "rb")
a = data.read(1062)
data.close()
a = a.split(b"\n\n")
n = bytes_to_long(a[0])
c = bytes_to_long(a[1])
p = gmpy2.iroot(n // pow(20210123, 6), 16)[0]
q = findPrime(20210123 * p * p)
r = findPrime(p * q * q)
s = findPrime(p * q * r)
assert n == p * q * r * s
phi = (p-1)*(q-1)*(r-1)*(s-1)
e = 0x10001
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
print long_to_bytes(m)
flag
flag{we11_y0U_aRe_n07_AFra1d_0F_nA1VE_R5A_NOw~}
Reverse
A-Maze-In
WriteUp
打开附件,得到一个exe
,运行游戏发现摸不着头脑,只说输入个way
。
于是ida
看看,根据经验找到main
为sub_4011B0
(没经验的话动态跟一下或者一个一个点进去都行)。
发现一堆判断输入合法性的东西,那挺好,有目标了,首先是长度34
,然后挨个判断字符是否是UDLR
的其中一个,是的话对比自身数据,数据是1就没事,不是1直接over。然后纵向U-D+
横向L-R+
计算类似坐标的东西,最后需要落在横4纵7
的位置上才算合法,不然通通over。
刚开始我以为这题是拿深搜递归做的,跑了半天愣是没一个结果,后来等不下去了就来手动做了。
那就继续看,最后有个关键一判sub_401090
,具体干啥的我也没看懂。
emm还是看上面对数据的判定吧,下标4 * (横 + 8 * 纵)
看着就很暗示,然后结合题目的maze
猜测应当就是走路题没跑了,于是可以推测出应当是8*n
的迷宫,然后这个4 *
的话,可以配合byte_404018
byte_404019
byte_40401A
byte_40401B
这种连号的data来理解,可以猜测数据格式应当是4字节1组,分别是上 下 左 右
,那么咱感觉去康康数据是不是和猜的一样。
0 1 0 1 0 1 1 1 0 0 1 1 1 0 1 1 0 1 1 0 0 1 0 1 0 1 1 0 0 1 0 0
1 0 0 0 1 0 0 1 0 0 1 1 0 1 1 0 1 1 0 0 1 0 0 0 1 1 0 0 1 1 0 0
0 1 0 1 0 0 1 1 0 0 1 1 1 0 1 0 1 0 0 1 0 1 1 0 1 0 0 1 1 1 1 0
1 0 0 1 0 1 1 0 0 1 0 1 0 0 1 1 0 1 1 0 1 1 0 0 0 1 0 1 1 0 1 0
0 1 0 1 1 0 1 0 1 0 0 1 0 1 1 0 1 1 0 0 1 1 0 0 1 0 0 1 0 1 1 0
1 1 0 0 0 1 0 1 0 0 1 1 1 0 1 0 1 1 0 0 1 1 0 0 0 1 0 1 1 0 1 0
1 1 0 0 1 0 0 1 0 1 1 0 0 1 0 1 1 0 1 0 1 1 0 0 1 0 0 1 0 1 1 0
1 0 0 1 0 0 1 1 1 0 1 0 1 0 0 1 0 1 1 0 1 0 0 1 0 0 1 1 1 0 1 0
似乎确实可以按刚才猜的来分割数据,分割出了个8*8
的地图,再加以验证一下,最左8个点位的左
路全是0
,所以确实是不通的,最右8个点位的右
路全是0
,所以也确实是不通的,最后在验证最上8个点位的上
路和最下8个点位的下
路的时候,发现有几个点会溢出去,地图没做封闭,所以这似乎可能就是之前我用递归脚本跑不出来的原因?
那么既然地图已经完整了,那咱就把地图的路给画出来呗,于是得到如下草图。
根据之前源码里看到的起始点位横3纵0
,发现确实可以把出入口都给对应上,而且路就一条,那么冲就完事了,得到输入路径LLDRRDLLLDRDLDDDRRULURRULURRDDDLDR
,于是去连上服务器,交互一下得到flag。(事实证明那个sub_401090
的判断就是虚晃一枪,让服务器去判就完事了,咱不care)
flag
flag{6b5d489e-c461-4512-8254-6016a92be9be}
Matara Okina[一血]
WriteUp
刚开始拿到题,我是懵逼的,这题是的main
咋这么短,难道又是分析so
的题?我tm直接放弃。
但是还是去看了一下另一个叫flag
的activity
,似乎就是交互flag相关的代码,于是看看,发现根本看不懂,就发现有个需要异或一下@lgvjocWzihodmXov[EWO
的验证,然后顺手百度了一下scheme
是个啥,因为感觉有点眼熟,于是嘛,好家伙,SchemeUrl
啊,那这题思路有了,跟web一样构造出SchemeUrl
,然后调用就完事了。
于是去查了一下SchemeUrl
的相关参数是怎么设定的,因为没在源码里看到,就一个?secret=
是已经知道的。
于是就找到了在AndroidManifest.xml
的SchemeUrl
参数定义。
n那么拼一下就能得到sh0w://p4th/70/1nput?secret=
这么一个SchemeUrl
,然后把上面的@lgvjocWzihodmXov[EWO
给再按特定方式xor
一下得到Android_scheme_is_FUN
拼上去,一个崭新的payload就构造好了。
byte = bytearray(b'@lgvjocWzihodmXov[EWO')
i = 0
while i < (len(byte) + 1) // 2:
i2 = i + 1
byte[i] = byte[i] ^ i2
length = len(byte) - 1 - i
byte[length] = byte[length] ^ i2
i = i2
print(byte)
接着随便找个能调SchemeUrl
的东西就行了,我用的是Anywhere-
。
调一遍,直接得到flag。
flag
flag{sh0w://p4th/70/1nput?secret=Android_scheme_is_FUN_1635b71e036d}
UnrealFlag[三血]
WriteUp
拿到附件,解压发现是个拿UE4
做的3D程序,但是并没有显示flag。
不过既然提示了flag在pak
里,那么就去解包呗,这不是UE
、Unity
、Spine
这类游戏引擎的基本操作嘛。
但是拿知名UE
解包工具umodel
解包一下,发现存在加密,需要提供key。
于是...上谷歌呗,找到了Reverse Engineering AES Keys From Unreal Engine 4 Projects这个文章,虽然差了好几个版本,但是大版本都是UE4
,应该差不多,于是就开始抄作业呗。
先是x64dbg
调试.\FindFlag\Binaries\Win64\FindFlag-Win64-Shipping.exe
这个文件,直接搜字符串Corrupted index offset in pak file.
,大概会出现俩结果,俩个都进去在相关跳转的外部下个断(因为下在跳内部的根本到不了断点,咱这是报错文本,前提是报错才进去)然后哪个断住了哪个就是咱需要的地方。
然后往下找call点,找到和下图差不多的(周围的汇编代码都差不多)就是关键的和key有关的函数入口了。
跟进去,然后在这个函数的结尾处找到传参寄存器是r8
、edx
、ecx
的call点。
然后找到r8
指向的内存,就能拿到key的16进制形式B9 91 67 8A C1 A6 F4 01 5D 43 68 46 04 C4 4A 9B 7F 3E 2C 04 A1 82 46 C4 30 93 A7 F1 BB FF 6A B7
,然后把hex
转成base64
形式就得到了uZFnisGm9AFdQ2hGBMRKm38+LAShgkbEMJOn8bv/arc=
。
之后试了一下umodel
和UnrealPakViewer
解包,一个说key不对一个直接闪退,搞得我一度怀疑是我找错了key。
于是就跑epic官网下了个虚幻引擎,用安装目录里面的.\Engine\Binaries\Win64\UnrealPak.exe
去解包。
需要把key放在一个json里。
{
"$types": {
"UnrealBuildTool.EncryptionAndSigning+CryptoSettings, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null": "1",
"UnrealBuildTool.EncryptionAndSigning+EncryptionKey, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null": "2"
},
"$type": "1",
"EncryptionKey": {
"$type": "2",
"Name": null,
"Guid": null,
"Key": "uZFnisGm9AFdQ2hGBMRKm38+LAShgkbEMJOn8bv/arc="
},
"SigningKey": null,
"bEnablePakSigning": false,
"bEnablePakIndexEncryption": true,
"bEnablePakIniEncryption": true,
"bEnablePakUAssetEncryption": false,
"bEnablePakFullAssetEncryption": false,
"bDataCryptoRequired": true,
"SecondaryEncryptionKeys": []
}
UnrealPak FindFlag-WindowsNoEditor.pak -Extract ExtractDir -cryptokeys=crypto.json
(ExtractDir
和crypto.json
需要填绝对路径,不然如果异目录执行还是会跑到UnrealPak
目录下去,应该是bug)
成功解包出东西来了,有Engine
和FindFlag
俩文件夹,直觉告诉我flag在后面这个文件夹。
FindFlag
│ AssetRegistry.bin
│ FindFlag.uproject
│
├─Config
│ DefaultEngine.ini
│ DefaultGame.ini
│ DefaultGameUserSettings.ini
│
├─Content
│ flag.uasset
│ flag.uexp
│ ShaderArchive-FindFlag-PCD3D_SM5.ushaderbytecode
│ ShaderArchive-Global-PCD3D_SM5.ushaderbytecode
│ Untitled.uexp
│ Untitled.umap
│
└─Plugins
FindFlag.upluginmanifest
然后直觉又告诉我flag.uasset
和flag.uexp
应该就是最后的抵抗了,冲!
用JohnWickParse
继续二次解包,先看一下里面有啥。
john-wick-parse serialize flag
得到个json文件打表。
[{"export_type":"Texture2D","ImportedSize":{"x":1440,"y":128},"LightingGuid":"3e2af41544ba5ffc9e66b6b7d4f60cf5","NeverStream":true}]
一看,哦吼,就一个文件,而且是Texture2D
也就是图片格式,那么冲!
john-wick-parse texture flag
拿到flag.png
。
flag
flag{tHe_sKiLl_of_UnpAck1n9_UNrea1}
anniu
WriteUp
简单题,直接用灰色按钮克星
就行。
flag
flag{huiseanniuyeyoukexing}
warmup
WriteUp
乍一看,好像挺简单的,输入flag内容,判长==48,sub_11F5
就是个判断内容合法的,只要全是/[0:9a:f]/
范围内的就行。
然后sub_125E
看一下的话,其实用python
总结一下就是下面这个功能。
t = 0
for i in range(16):
for j in range(16):
if byte_40A0[16 * i + j] == 0xff:
byte_40A0[16 * i + j] = sub_1224(flag[t])
t += 1
也就是把byte_40A0
里的0xff
按顺序替换成flag的内容,那么可以猜测有48个0xff
。
于是去看一下byte_40A0
是啥。
08 0E FF 0C 09 0D FF 01 0A 0F 03 0B 00 02 FF 04
01 06 03 02 05 0A 07 00 08 09 FF 04 0F 0E 0B 0D
0A 00 FF 0D 04 0F 03 0B 07 05 0E 02 06 08 0C 01
04 0B 05 0F FF 02 FF 0C 06 0D 01 00 FF 0A 03 09
02 0A FF 03 0D 00 0B 05 0C FF 09 01 FF 0F 07 0E
0D 07 0C 0B 0F 0E 0A 08 00 FF 05 03 09 06 01 02
FF 01 0F FF 0C 09 04 06 02 0E 0D FF FF 03 0A FF
09 04 06 0E 02 07 01 03 0B 08 0A 0F 05 FF 00 0C
FF 03 0A 07 0E 08 0C 04 09 FF 00 0D 02 FF 06 FF
0C 09 01 FF 0B 03 0F 0D 0E 0A FF FF 08 00 04 07
06 0D 00 08 0A 01 02 FF FF 07 04 05 0C 0B FF 0F
0B 02 0E FF 00 FF 05 FF 0F 01 FF 0C 0A 09 0D 03
FF 0F 0B FF 03 0C FF 0E 05 FF FF 09 FF 04 08 0A
0E 08 FF FF 07 05 0D 0F 04 03 FF FF 01 0C 09 00
FF 05 0D 09 06 04 08 0A 01 0C 0F 0E FF 07 02 0B
03 FF 04 0A FF 0B 09 02 0D 00 FF 08 0E FF 0F 06
emm,一眼看感觉是单区域的16*16
数独...然后flag就是要填的数,好像很有道理,那就这么干呗(懒得接着看验证flag的代码了),先把能直接根据行和列缺少的数推测出来地方直接填上,为了区别本来就在的数,我把高位写成X
。然后再把每行和每列的缺数统计出来。
为了方便看哪行哪列缺啥,我在下面写了一行00~0F
的亮灯用的(就是双击能高亮,我都佩服我这个操作)
然后在每个空处取一下行列的缺数交集,如果交集就一个数,那就能得出该填啥,如果有多个数,那就先把其他空填了,最后回来再看一遍交集。
如此往复几次,就把数独填好了。
然后用正则把填的数提取出来就完事。
import re
result = """08 0E X7 0C 09 0D X6 01 0A 0F 03 0B 00 02 X5 04
01 06 03 02 05 0A 07 00 08 09 XC 04 0F 0E 0B 0D
0A 00 X9 0D 04 0F 03 0B 07 05 0E 02 06 08 0C 01
04 0B 05 0F X8 02 XE 0C 06 0D 01 00 X7 0A 03 09
02 0A X8 03 0D 00 0B 05 0C X6 09 01 X4 0F 07 0E
0D 07 0C 0B 0F 0E 0A 08 00 X4 05 03 09 06 01 02
X5 01 0F X0 0C 09 04 06 02 0E 0D X7 XB 03 0A X8
09 04 06 0E 02 07 01 03 0B 08 0A 0F 05 XD 00 0C
XF 03 0A 07 0E 08 0C 04 09 XB 00 0D 02 X1 06 X5
0C 09 01 X5 0B 03 0F 0D 0E 0A X2 X6 08 00 04 07
06 0D 00 08 0A 01 02 X9 X3 07 04 05 0C 0B XE 0F
0B 02 0E X4 00 X6 05 X7 0F 01 X8 0C 0A 09 0D 03
X7 0F 0B X1 03 0C X0 0E 05 X2 X6 09 XD 04 08 0A
0E 08 X2 X6 07 05 0D 0F 04 03 XB XA 01 0C 09 00
X0 05 0D 09 06 04 08 0A 01 0C 0F 0E X3 07 02 0B
03 XC 04 0A X1 0B 09 02 0D 00 X7 08 0E X5 0F 06"""
print("".join([i for i in re.findall("X([0-9A-F])", result)]).lower())
就能拿到flag的内容,俩头套一下格式就完事。
flag
flag{765c98e78644507b8dfb1552693e467871026d26ba03c175}
Comments 1 条评论
博主 加菲·
太强了