Drunkmars's Blog

记一次php、sql注入学习后的一道ctf代码审计题复盘

字数统计: 2.4k阅读时长: 10 min
2021/03/27

之前在学校的时候参加了ctf比赛,有幸拿了一个代码审计题的一血,但是当时可能也是算运气比较好吧,因为我的思路并不是常规解法,但还是拿了一血,现在更深入的学习php和sql注入之后,反过来看这道代码审计题,又有一种豁然开朗的感觉。虽然一度到了第二名,但是最后还是没有稳住,不过在我们队伍前面的都是大三的学长,这也不算特别难受吧。

在这里插入图片描述

初步思路

这个题目是个源码泄露的题,用WWW.zip就能够将他的代码下载到本地,如下:

在这里插入图片描述

拿到之后以当时的技术水平肯定是审计不出来的,所以直接上了sexy代码审计系统审计:

在这里插入图片描述

审计结果如图,首先我排除了3,因为xss漏洞必须要有道管理员点击之后才会生效,所以直接放弃,那么突破这道题目的关键就放到前面两个点上,找到核心的functions.php代码如下:

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
55
56
57
58
59
60
61
62
63
64
65
66
<?php
error_reporting(0);
session_start();
include("config_ini.php");

function escape1($string){
$a = 1;
$b = 2;
global $link;
$string = $link->real_escape_string($string);
return $string;
}

function check($seesee){
$seesee = trim(escape1($seesee));
if(strlen($seesee)<5){
die("你谁啊你,你就想登录");
}
if(strlen($seesee)>11){
$seesee = substr($seesee, 0, 11);
}
return $seesee;
}

function Drawtarget($file,$path){
$zipfile = new ZipArchive;
if ($zipfile->open($file) === TRUE) { //解压zip文件
$zipfile->extractTo($path);
$zipfile->close();
}
}
function SeeseeDir($path) {
$papers = scandir($path);
foreach ($papers as $document) {14
$address = "$path/$document";
if (is_file($address)) {
$section = pathinfo($document);
$tailoring = strtolower($section['extension']);//获取扩展名判断是否删除
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","pHp","pHp5","pHp4","pHp3","pHp2","Html","Htm","pHtml","jsp","jspa","jspx","jsw","jsv","jspf","jtml","jSp","jSpx","jSpa","jSw","jSv","jSpf","jHtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","aSp","aSpx","aSa","aSax","aScx","aShx","aSmx","cEr","sWf","swf","htaccess","ini");
if (!in_array($tailoring, $deny_ext)) {
@chmod($address, 0666);
} else {
@chmod($address, 0666); // 防止连接意外掉线
unlink($address);
}
} elseif ($document != '.' && $document != '..' && is_dir($address)) {
CheckDir($address);
}
}
}

function check_lg(){
if($_SESSION['logged']!=1){
die("请先登录");
}
}
function check_ad(){
if($_SESSION['admin']!="True"){
die("啊,只有管理员才可以哦!");
}
}
function send_to_phone($randomssss){
$phone_number="1111111111";
//发送随机的验证码到管理员的手机
}
?>

管理员后台login.php的代码如下:

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
<?php

include("config_ini.php");
include("functions_ini.php");

session_start();

$user = $_POST['username'];
$pass = $_POST['password'];

$user = check($user);
$pass = check($pass); //这里不行~


$sql = "SELECT username, password FROM ctf2020 WHERE username='" .$user ."' && password='" .$pass ."'";
$res = $link->query($sql);


if ($res->num_rows > 0 or $_SESSION['logged']==1){
$_SESSION['logged'] = 1;
header("Location: admin.php");
}
else{
echo "小老弟,就这还谈恋爱?"."<br>";
}

$link->close();


?>

这段代码在当时的我看来是一头雾水,一开始用bp去爆破管理员登录后台无果

在这里插入图片描述

然后我的直觉就是sql注入进管理员后台,于是跟着saxy审计出来的php去找sql漏洞,remote_admin.php代码如下:

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
<?php


include "functions_ini.php";
session_start();

check_lg();
error_reporting(0);
# 管理员想远程访问后台时使用
$remote_check_admin = create_function("",'if(isset($_SERVER["HTTP_CHECK_ADMIN"])){$_SERVER["REMOTE_ADDR"] = $_SERVER["HTTP_CHECK_ADMIN"];}');

mt_srand(time());
$rdm = mt_rand();
$rdmrd = $rdm.time();

eval("function admin_$rdmrd() {"
."global \$remote_check_admin; \$remote_check_admin();"
."}");

send_to_phone($rdmrd); //生成随机动态的验证码发给管理员


$_GET['rdmrd']();
if($_SERVER['REMOTE_ADDR']=="127.0.0.1"){
$_SESSION['admin'] = "True";
echo "欢迎您admin,即将为您跳转到后台";
sleep(3);
echo "<script> alert('欢迎您admin,即将为您跳转到后台');parent.location.href='./admin.php'; </script>";
}
?>

找到eval("function admin_$rdmrd()这个地方对应上去找到的是create_function()这样一个匿名函数

在这里插入图片描述

然后百度大法发现这个地方果真有所谓的后门漏洞:

在这里插入图片描述

但是很遗憾,这个地方即使有匿名函数这个漏洞,但是还是不能够通过这个地方实行sql注入,原因就在functions_ini.php的这个地方:

在这里插入图片描述

这个地方他使用了一个real_escape_string()的匿名函数,这个匿名函数可不得了,这个函数一出,可以说几乎断绝了sql注入的一切可能,他的功能简单一点说就是在所有的可注入符号后面加一个转义符(\),也就是如果你用id=1’进行注入,他会自动在id=1’后加一个转义符,即变成id=1‘\,也就是说’会被转义掉,就不能进行sql注入。

突破点

这个地方在经过了这么久的学习之后,如果这个地方是采用的gbk编码,那么宽字节注入应该也是行得通的。这个地方其实当时是卡了很久的,甚至一度放弃了sql注入的想法,但是在后面仔细对代码一个函数一个函数的查功能之后,终于发现了一个让我兴奋的点,

real_escape_string()substr()这两个函数一起使用,那么就存在了一个问题:

substr() 函数返回字符串的一部分
如:echo substr(“Hello world”,10),返回的值是d
echo substr(“Hello world”,2),返回的值就是llo world

那么再看一下这段代码:

在这里插入图片描述

这里用substr()截取seesee这个变量,因为如果在字符串的后面加上任意一个符号,real_escape_string()都是会将它加上一个转义符的,但是这里substr(\$seesee,0,11)只截取0-11取11位,那么如果我构造一个“aaaaaaaaaa\”这个字符串,通过real_escape_string()这个函数转义之后就会变成12位:
aaaaaaaaaa\\” ,但是substr()这个函数只取11位,那么 \ 就会跟着传进sql语句中执行,达到绕过real_escape_string()的效果,构造密码为”or+2>1#“,这时候的sql语句就应该为:

在这里插入图片描述

1
2
SELECT username, password FROM ctf2020 WHERE username='aaaaaaaaaa\' &&
password='or+2>1#'

进到登录页面输入帐号和密码

在这里插入图片描述

ok第一关已经被攻破,进入了他的后台

绕过管理员权限

进入了后台,但是这个地方显示要管理员才能够继续操作,一开始想的是利用bp构造一个127.0.0.1的本地xff,然而无果,又只有审计代码,还是回到了最先找到漏洞的那个地方

在这里插入图片描述

这个地方因为create_function()这个函数的漏洞,能够构造一个匿名函数来伪造REMOTE_ADDR

科普一下REMOTE_ADDR

1
是你的客户端跟你的服务器“握手”时候的IP。如果使用了“匿名代理”,REMOTE_ADDR将显示代理服务器的IP

碰巧看到一个相似的ctf题目如下:

在这里插入图片描述

构造一个\x00lambda的匿名函数通过bp爆破进入管理员后台

bp抓包lambda函数的变量,因为是本地管理员登录,加上一个check-admin:127.0.0.1

在这里插入图片描述

这里我写了一个1-1000的字典对其进行爆破,只要返回值为200即响应成功

在这里插入图片描述

关掉bp的抓包刷新页面后如图所示:

文件上传绕过黑名单

这个地方第二关也已经绕过了,进入了他管理员的界面,接下来就是文件上传了,但是这个文件上传很贼,先看一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function SeeseeDir($path) {
$papers = scandir($path);
foreach ($papers as $document) {14
$address = "$path/$document";
if (is_file($address)) {
$section = pathinfo($document);
$tailoring = strtolower($section['extension']);//获取扩展名判断是否删除
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","pHp","pHp5","pHp4","pHp3","pHp2","Html","Htm","pHtml","jsp","jspa","jspx","jsw","jsv","jspf","jtml","jSp","jSpx","jSpa","jSw","jSv","jSpf","jHtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","aSp","aSpx","aSa","aSax","aScx","aShx","aSmx","cEr","sWf","swf","htaccess","ini");
if (!in_array($tailoring, $deny_ext)) {
@chmod($address, 0666);
} else {
@chmod($address, 0666); // 防止连接意外掉线
unlink($address);
}
} elseif ($document != '.' && $document != '..' && is_dir($address)) {
CheckDir($address);
}
}

重点看一下$deny_ext这一行,这一行代码就是限制上传的文件类型,可以说其实已经做得很完美了,无论是大小写的文件都已经被过滤掉了,不过出题人还是忘过滤了一个文件类型,就是php7(php是世界上最好的语言,QAQ)

那么直接构造一个php7的一句话木马,压缩成zip

在这里插入图片描述

试着上传一下,果然没有出错,返回了文件上传的路径

直接访问这个路径,然后hackbar post传参,直接拿到flag

当时有几个地方其实是靠学长的提示才能够继续往下进行,但是总体来说的思路还是没有错的,经过一段时间的学习之后,现在回过头来看这些php代码又有了一种豁然开朗的感觉

CATALOG
  1. 1. 初步思路
  2. 2. 突破点
  3. 3. 绕过管理员权限
  4. 4. 文件上传绕过黑名单