freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

YAK-SSA,古希腊掌管PHP代码审计的神
yaklang 2025-01-10 16:33:43 89670
所属地 四川省

之前的文章中曾为大家简单介绍过

线上代码审计平台ssa.to

今天牛牛就来为大家详细介绍一下

如何用ssa进行PHP的代码审计

1736497525_6780d975a952f19baa1ff.jpg!small

1736497544_6780d9888e75dfdeebbc4.png!small

1736497550_6780d98e1cd2e53546b64.png!small

1736497556_6780d9948400fc8bfcca1.png!small

tp封装的辅助参数,I/request方法

thinkphp中的封装了请求对象,$request

路由参数绑定

在thinkphp中,I方法的过滤并不是时时刻刻都会起作用,在底层中,I如果没有显示的指定过滤参数,其过滤参数需要去config文件中去指定,如果也没有指定,就和传统的参数获取没有区别。
图片
在tp中,会涉及到一些底层API的封装,开发者不必利用底层API的实现,只要学会使用即可。所以在代码审计的时候,可以找到这些函数,实现从source点到sink点的路径缩短。实现更加精确的数据流分析。
数据库相关:

Db::table() xx->insert() xx->update() xx->delete() $model::where() Db::transaction()

文件上传相关:
在tp底层中,常见的文件上传辅助函数有:

$file->move()
$file->check()
$file->save()
Filesystem()等
$file->error()
$file->getError()

1736497595_6780d9bb6e79a54e7b1c7.png!small

1736497598_6780d9be623985b30ab08.png!small
在之前的文章中,会涉及到很多规则编写部分,而ssa官网也提出了syntaxflow的简单实用,重点功能为:

$source #-> as $sink //顶级定义 $source --> as $sink //底级实用 其次:比较重要的两个config include: 路径上有(包含) exclude: 路径上无(排除)

其次:比较重要的两个config
include: 路径上有(包含)
exclude: 路径上无(排除)
在上面的基础上,可以配合过滤器的使用和集合运算,来实现更多的自定义规则

简单的规则编写:
在寻找顶级定义的过程中排序路径上的某些内容

<?php $a = 1; if($a){ $a = filter($a); }else{ $a = unsafe($a); } eval($a);

画出来的控制流图为:
1736497641_6780d9e903f969e2a2c1a.png!small

一共有两个分支到达done block,想到无过滤器的一条路径,规则如下:

//寻找顶级定义的过程中,排除路径上filter的 *a #{exclude: `*?{have: filter}`}-> as $sink /* $sink: t75: Undefined-unsafe .:7:10 - 7:16 t66: 1 .:3:6 - 3:7 */

但是会发现,这个并不是想到的内容,原因就出现在,我们需要让它与可控变量做交集 。需要让它的顶级调用者中,出现可控变量,这样的话,再次改写代码。

// 寻找顶级定义的过程中,路径上包含与_POST相交的点,并且排除filter _POST as $source *a #{exclude: <<<CODE *?{have: filter} CODE, include: <<<CODE * & $source CODE }-> as $sink

在这样的一个简单demo学习之后,我们就可以进入实战演示。
1736497671_6780da072758518bbc3b6.png!small

在github中找到一个cms,在yakssa中进行编译。

在编译过程中,会发现,代码多之后编译的很慢,这是因为在php中会有依赖,像go一样,而这些依赖一般都是用来辅助开发者去完成某项功能,比如imageutil、wechat等依赖,可以在编译的过程中,跳过这些依赖的编译,虽然代码不是完整的,但是ssa也可以实现审计。

1736497735_6780da47bd7366d674d3a.png!small
搜索thinkphp中常见的可控变量。然后写成一条规则。

request() as $source1
input() as $source3
i() as $source4
*request.get() as $source5
*request.post() as $source6
.input() as $source7
.param() as $source8
.post() as $source9
$source1+$source3+$source4+$source5+$source6+$source7+$source8+$source9 as $source

1736497724_6780da3cea8558c637d08.png!small
xss分为很多种,这里以最简单的反射和存储来进行说明。

  • 反射xss :由用户手动控制,直接输出到页面。
  • 存储xss:由用户手动控制,但存储到数据库中。

这里写一条存储xss的sf语句。

request() as $source1
input() as $source3
i() as $source4
*request.get() as $source5
*request.post() as $source6
.input() as $source7
.param() as $source8
.post() as $source9
$source1+$source3+$source4+$source5+$source6+$source7+$source8+$source9 as $source

.add() as $func5
.save() as $func6
$func5 +$func6 as $func

$func #{include: `* & $source`}-> as $sink

审计出来的内容很多,这里我们一一查看。

1736497785_6780da7931b96ee9de704.png!small
定位到system.php文件中,先做了请求判断,如果有的话返回已经存在,如果没有的话,将内容进行添加,这里跟进发现。

1736497789_6780da7d2f91351894bc3.png!small
会将最后的内容存放到数据库中。

1736497794_6780da82eb7538e460baa.png!small

注意:不是所有的I方法都有漏洞,在之前tp的代码审计中提到,参数可以配置默认过滤。

此项目中,因为没有配置默认过滤,可以直接根据路由传入参数。

POST /index.php/admin/sys.Auth/groupAdd HTTP/1.1
Host: 10.211.55.11:8092
Upgrade-Insecure-Requests: 1
Accept-Language: zh-CN,zh;q=0.9
Referer: http://10.211.55.11:8092/index.php/admin/index
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Cookie: think_lang=zh-cn; PHPSESSID=655381238c1f39ebb2ccbc7cb5d7830e; Hm_lvt_d7fcc824c81abdf6e6d33ffc0e10c071=1736077733,1736168831; Hm_lpvt_d7fcc824c81abdf6e6d33ffc0e10c071=1736168831; HMACCOUNT=8C60E645EC57F215
Connection: keep-alive
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded

title=<script>alert(1)</script>

根据syntaxflow找到的内容,基本上都有xss漏洞,至少3处。
1736497843_6780dab3c4b6db9888d29.png!small

.request().file() as $source1
request().file() as $source2

$source1+$source2 as $source

$source

定位到文件upload.php中,可以看到是过了一层校验,thinkphp validate,这里可以去看下,简单来说就是对数组进行过滤。

1736497866_6780dacaaa0dee4b36c9a.png!small
此处的过滤器写的有问题,正确的过滤器如下。

1736497875_6780dad30a7d3c3533a73.png!small
上传文件即可。

1736497879_6780dad7ca36050bcf76b.png!small

1736497885_6780dadd9e846e920fd9c.png!small
通过对输入点的审计,找到这样一段代码。

1736497893_6780dae57c2bf7bf29ba6.png!small

  • 时间戳校验
  • appid校验
  • 签名校验

1736497901_6780daed9c529a651d2f1.png!small
是根据输入来计算的MD5(可以使用热加载来写)

1736497907_6780daf3a2069b6dbb0fe.png!small
而数据库中有默认的appid和appsecret。

1736497913_6780daf91cf30697867fa.png!small
判断是否有用户名,并且进行密码校验。

1736497916_6780dafcb5eb2c09a27a4.png!small

// 使用标签 {{yak(handle|param)}} 可触发热加载调用
handle = func(param) {
    // 在这里可以直接返回一个字符串
}

// 使用标签 {{yak(handle1|...)}} 可触发热加载调用
handle1 = func(param) {
    // 这个特殊的 Hook 也支持返回数组
    return ["12312312", "abc", "def"]
}

// beforeRequest 允许发送数据包前再做一次处理,定义为 func(origin []byte) []byte
beforeRequest = func(req) {
    /*
        // 我们可以提供一些基础用法,比如说单纯就是替换一个时间戳~
        req = str.ReplaceAll(req, "TIMESTAMP_INT64", sprint(time.Now().Unix()))
    */ 
params = poc.GetAllHTTPPacketPostParams(req)
params.Delete("sign")
params["key"] = "123456"
var buildSign = ""
for k,v in params{
    buildSign = buildSign+sprintf("%s=%s", k,v)
    buildSign = buildSign+ "&"
}
buildSign = buildSign[:-1]
params.Set("sign", codec.Md5(buildSign))
println(params)
return poc.ReplaceAllHTTPPacketPostParams(req, params)
}

// afterRequest 允许对每一个请求的响应做处理,定义为 func(origin []byte) []byte
afterRequest = func(rsp) {
    return []byte(rsp)
}

// mirrorHTTPFlow 允许对每一个请求的响应做处理,定义为 func(req []byte, rsp []byte, params map[string]any) map[string]any
// 返回值回作为下一个请求的参数,或者提取的数据,如果你需要解密响应内容,在这里操作是最合适的
mirrorHTTPFlow = func(req, rsp, params) {
    return params
}


1736497954_6780db22a81f8bd43e6ae.png!small

1736497970_6780db32c4584f8d32996.png!small
PHP代码审计——ThinkPHP基础

END
YAK官方资源

Yak 语言官方教程:
Yak:致力于安全能力融合的语言 | Yak Program Language

Yakit 视频教程:
YakProject的个人空间-YakProject个人主页-哔哩哔哩视频

Github下载地址:
GitHub - yaklang/yakit: Cyber Security ALL-IN-ONE Platform

GitHub - yaklang/yaklang: A programming language exclusively designed for cybersecurity

Yakit官网下载地址:
https://yaklang.com/

Yakit安装文档:
下载安装与更新配置 | Yak Program Language

Yakit使用文档:
Yakit: 集成化单兵安全能力平台 | Yak Program Language

常见问题速查:
FAQ | Yak Program Language

# php # 代码审计 # yakit
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 yaklang 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
Yak Project
yaklang LV.8
做难而正确的事!
  • 148 文章数
  • 102 关注者
Oi,不要小看Proxy浏览器插件与Yak之间的羁绊啊!
2025-02-21
震惊!YAK-JWT靶场的通关方式竟然是...
2025-02-17
成魔成仙,牛牛自己说了算!
2025-02-08