2025 京麒CTF 逆向部分WP

2025 京麒CTF 逆向部分WP

封面:X@yas

感觉自己有点不行了

risk

这题拿到后,可以看到Java层调用了stringFromJNI对输入进行了验证

image-20250525123226358

查看so文件,可以发现是静态注册,首先验证了输入的字符范围为数字,然后分别使用函数加载__lI11lli1entry两个文件

image-20250525123531553

至于内部怎么加载,我们可以不用考虑,想办法找到加载进去后这部分的代码就行,就上图的v23、v22这些地址的代码。通过Frida hook可以得知加载代码的函数只会在第一遍加载assets里的14个文件时调用14次,第二次不会再次加载,因此我当时考虑的是加载进去后通过什么方法把这部分代码dump出来分析。没有考虑直接使用IDA动调的原因主要是之前使用IDA动调基本上都是直接报SIGSEGV,调不下去,这次拿A15的Pixel6验证了下也是这样,我一直以为是IDA的问题,自从学了Frida后好久都没使用过IDA 动调了。

image-20250525124951138比赛结束后Pangbai酱说是用IDA动调做出来的,我开始怀疑是我自己的问题。更换过历代IDA版本进行测试,问题依旧,于是换设备,换成Mi10 A13,结果就能正常动调了。感觉是A15运行时有变更,IDA9都尚未支持的原因

image-20250525125142450

动调进去以后题目就很容易了

image-20250525130202594

4个加密,要求加密完跟上面计算得到的值一样,这里动调可以得到4组加密值(上图的v379、v11、v10、v9)。

4个函数每个内部都是类似的构造,只是大数不同

image-20250525131030704

函数里将大数变成DWORD List,List里每个数对应一个十进制位进行运算加密。用户输入的也是一个大数,作为pow的m,即RSA的明文。目前已知由同一个明文通过4个模数加密得到的4个密文,e=11

这时候拷问AI可以得知,可以采用CRT+Hastad广播攻击的方式恢复明文,找个脚本跑一下

image-20250525131340461

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import gmpy2
from functools import reduce
import base64
import libnum


def CRT(mi, ai):
assert (reduce(gmpy2.gcd, mi) == 1)
assert (isinstance(mi, list) and isinstance(ai, list))
M = reduce(lambda x, y: x * y, mi)
ai_ti_Mi = [a * (M // m) * gmpy2.invert(M // m, m) for (m, a) in zip(mi, ai)]
return reduce(lambda x, y: x + y, ai_ti_Mi) % M


e = 11
n = [
93386350790648274292693565965720799143100762212309344483909867417505553322101882276812366885839372829048973009669445498314766714854176333852787885722707400381289599567301651307174890668895387705303313515848184649012370525278402741914174927176480001617738135588704351371381633923186037714185167913274820912703,
76954053999406809199039236767709342591876556552749506913564932498600772999708832034488452836837990449957740494844378018903888038527521616666509133672527409707775727422384156306545835267999151542912949163659926050514075680896339410657789375090417535549805943430969851914063454508412664646645448454513550708101,
69324581698645422224068313194678831065177009479786444971009461562497358976600230674977634134219251381126451236847896472567379586099142546477204757267867071377013283257737579326205814165020818856109731531246229703771562947402606126793115022780020308026377520775479673938236814010964729606917913290547179935841,
127760781426350734024590744201813139160070015961315159967418114266048890067154669145301269843698776667043711220002778259575059521393375665574786473650834040008339961314823364457449690439661702268245759247533617181783851053636045022200060540447636193778239479032237921652131614511096946376433113928718627696187
]
c = [
'CAUABwACAQcAAQUJBAMEAgcJAgAEBQQCBAgFCQcBAAMBAgMBBQMFBQgHBAEEAAAGBAAEBAYFAQkEAgcEAQMJBQgEBwUDCAkBBwkABwkIBggABgcDBAgGCAMBBQYABQQHCAEHBgkHCAkFCAUJAAQFBwkJAQMIAAEJBgQHCQkGBwQCBAIFBwUIAggICAkGAwYIBgYAAwEJAgEIAgEJAgAIBgIAAQgAAAEECAAJAgQCBwUGAQMHBQQIAAkJCAgIAgUDBAECCAkAAQYBBQcDAwADBAcBAAUFAwMGBQYAAQEFAAEFAAgCAQYDAwMGBQcFAgUJCAIIBwYIBgkHAgkFAQEBBwAJAQcJAAYBBwMHCQMIAgEGAwUBBQABAggBAQcFBgUBBwYECQUGBAEAAgQIAAMFAQYJAAY=',
'CQQGCAQFBwIBAggAAQIGBAcACQgICQYJAQYGBgIEBgEHAQQHAAkHBwcFCQMJAQAIAAEGBAkCCQIIBwUJAQIDAQUFBAEECQMDAAAIAwAICQcEAAkFCAEEAgcFBggFCQUFAgUIBAgABQAJCAIHBAMEAgIJAgQBBggEAgcJAwMIAQkBAwcFAgEECQIBBgcABAUEBQgDCAYBAgYEBgkEAwAAAgIACQkBBAkABQYFAQMDAgMIBQEDAgMCAAEBBAUJAgYJAQgFCAYBAwcGBgAIAgkECQIHAwcACAEGBgMACQQHAQcFBgEJAgADBQcDBQAGCAEDBAAJAQQECQYABgUGCQkFCQICAQgCBwgFAAABBwAACAQBBwkEAQEIBAcBBgUABwACBwQCBQcJBQUICAgHBgEFAwYBAgI=',
'BQgBAAYGBgIDAQEGBAQBBQMIBgMEAQIHAQUIAwEBBQkGCQgAAwcABwADCAMBBgEJAgMICAIBCQMJAAQDBwMCCQIHAgcFAwEGAAMGAgIJBgEHBQkGBgADAAAIBAEHAAEGAQkFAgEEBAIHBwcEBAkEAQQEAQIHAAgGCAcCAAYABQgFAgIGBAIBAgIAAAEJCAMCCQMIBgkHCQQCCQYAAgAGBwADBwgICQYHCQgHBAQJAQMJAAUCBwYJCQMIAwkBAAEJAwQHBwEAAwQCBQkJBgEBBwIIBwMGAQAACAEFAQYHAgAACAAJCQYGBgcCBwUAAAYCCQEIBgYDAAICBAAHBgcGBQAAAQMDBAMGCQYGBAMGBQQCCQQDBgEGAwkDCAEBAQIBAgUIAggBBQkBAQAAAQEFBAIJBgI=',
'AAIHBwEIAAICBQIJBwIEBgYABAgHAQEBAwIBAwgCBQQGAwYHBwYICQUFAQQJBAcEAwQAAAcFBwYBAgkHBwYHCQUAAgYDAAEFAgQDCQQEBAkJBgQCAAYACAgBBAcABgkIAgYCAgMEBAEFCQYHAwcHAwMIBgAAAAcJBwYFAgADAwIFAwcGBwQGCQEBAwIEAAcEBwkIBQEDCAAHBAgCAQgCAQgAAQQHAggCCAcEBAgGAwgEAAgBAAEIAAkGAQgBBQYEAgkEBAIBCAcCCAQCBwQCBgkGCQUACQgCBwkJBgIECQMAAgEAAQkAAQUJBwIBBwAHCAQDCQcHBgcAAgUDBwEGBwgBBwECAgcABAEDBwgFBQIHCAEBAgEJBAAEAwQECQAIBgAHBQUIAwIJAgQGCQkJBgAAAw=='
]

for i in range(len(c)):
c[i] = base64.b64decode(c[i])[::-1]
c[i] = int(''.join(str(b) for b in c[i]))

m = gmpy2.iroot(CRT(n, c), e)
if m[1]:
print(m[0])
print(libnum.n2s(int(m[0])).decode())
else:
print('Not Found')

得到解

1
2
103699236492935793328292813869377232280341419645113920874479429536584902501842193299041
5a95c9c2-c91f-421d-a7bb-9833c710116a

image-20250525131835241

从发现能动调进去到解出验证用时不到2个半钟

drillbeam

这道题感觉很适合研究一下去间接跳转和控制流平坦化混淆,mark一下

https://bbs.kanxue.com/thread-277086.htm

本文作者:lrhtony
本文链接:https://lrhtony.cn/2025/05/25/2025jqctf/
版权声明:本文采用 CC BY-NC-SA 4.0 协议进行许可