ctf php代码审计 绕过,Bugku CTF 之代码审计解题记录

前言

此笔记为在Bugku CTF平台上做代码审计题目的过程,也算是一篇wp吧

extract变量覆盖

extract($_GET):

$_GET:表示在传递参数时,URL通过get的方式传参,传输的数据以数组的形式封装在$_GET中

extract():从数组中将变量导入到当前的符号表。该函数使用数组键名作为变量名,数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量

file_get_contents():将整个文件读入一个字符串

trim()去除字符串两侧的空格或者指定字符trim(’string’,’string you want to delete’)

extract()函数的变量覆盖漏洞在于如果导入的数组中有与原来符号表中同名的变量,将会覆盖掉原有的变量,因此对于这道题,我们只需要构造payload?shiyan=&&flag=

此时我们就可以利用变量覆盖漏洞,使原有的flag漏洞的值被覆盖为空,再与我们传入的shiyan变量相比较,空等于空,因此得到flag

ef2dccb22b6c01b4461760053cf34af8.png

strcmp比较字符串

题目地址:

http://123.206.87.240:9009/6.php

5dea0da5476f6bd10b159110298ed765.png

由源码可以看出,题目通过GET方式传递参数a,然后将a和flag变量的值进行比较,若相等则输出flag的值。int strcmp(string str1,string str2)

如果str1小于str2返回0;如果两者相等返回0

这个函数存在一个漏洞就是当传入的参数不为字符串类型时,这个函数将发生错误,在php5.3之前,函数在显示错误信息后,将会返回0,因此我们可以传入payload?a[]=123

即可得到flag

48bde9c4190b2122ff18a84c52cf91d5.png

urldecode二次编码绕过int eregi(string pattern,string string,[array regs])

eregi()函数在一个字符串搜索指定模式的字符串。搜索不区分大小写,如果匹配成功则返回TRUE,否则返回FALSEurldecode()

urldecode()函数的作用就是将url编码后的字符串还原成原来的样子

因此这段代码的意思就是通过GET方式传入id参数,id的值不可以是hackerDJ,但是id的值在url解码后要为hackerDJ才可以得到flag,所以考虑用url二次编码绕过,在转换软件中对hackerDJ进行二次url编码后得到payload?id=%25%36%38%25%36%31%25%36%33%25%36%42%25%36%35%25%37%32%25%34%34%25%34%41

即可得到flag

9fd6220566b29a382bd29a98a7e749f1.png

md5()函数

题目地址:http://123.206.87.240:9009/18.php

题目源码如下:

c43b618abf6b731e1ccffc95647c8388.png

对题目中出现的函数进行分析md5()

md5()函数用于计算字符串的md5散列

这个题目的意思就是通过GET方式传入username和password参数,他们的值不能相等但是通过md5()函数计算后值要相等,这时可以想到md5()函数存在一个漏洞就是它无法处理数组型参数,因此构造payload?username[]=123&password[]=1234

即可得到flag

d593b3a9d7745afb8247f95c4a48e4f5.png

数组返回NULL绕过

题目地址:http://123.206.87.240:9009/19.php

题目源码如下:

fa9849e4c3ef922128365bc5aeddb2ac.png

对题目中出现的函数进行分析ereg ( string $pattern , string $string [, array &$regs ] ) : int

ereg()函数以区分大小写的方式在 string 中寻找与给定的正则表达式 pattern 所匹配的子串,题目代码中的正则表达式中的^代表字符串的开头,$代表字符串的结尾,+表示一次到多次,[a-zA-Z0-9]表示字符集strpos(string,find,start)

strpos()函数用于查找字符串在另一字符串中第一次出现的位置

这道题目的意思就是要通过GET方式传递一个password参数,这个参数只能包含大小写字母和数字,但同时还要有’–‘,这题我们可以考虑用%00绕过ereg()函数,构造payload?password=1%00--

可以得到flag

9b05e26b3b6dc87789808cc63d3ab21c.png

同时我们也可以使用数组绕过strpos()函数,构造payload?password[]=123

也可以得到flag

2d0c4fcf291fcb10381b899473a2ed93.png

弱类型整数大小比较绕过

题目地址:http://123.206.87.240:9009/22.php

题目源码如下:

47985a64c36179f2b6913255d45b3702.png

对题目出现的函数进行分析is_numeric($var)

is_numeric()函数用于判断传入的var变量是否为数字

这道题的意思很简单,就是用GET方式传入一个password变量,判断password是不是数字,如果是数字则返回空,接下来又让password和1336比较,当其大于1336时,输出flag,这里涉及到PHP的弱比较

* 弱类型比较:一般比较符号为”==”,弱类型比较在比较时若等号两边变量的类型不一样,则会先转换为同样的类型,再进行值的比较

* 强类型比较:一般比较符号为”===”,强类型比较在比较时会同时比较等号两边变量的类型和值

因此,我们可以构造payload?password=1337a

即可得到flag

d53d50db0838eb1d9e496f1cc2fdf5a7.png

sha()函数比较绕过

题目地址:http://123.206.87.240:9009/7.php

题目源码如下:

801984362f21ee6521e8470d415124a2.png

对题目中出现的函数进行分析var_dump( mixed $expression [, mixed $... ])

var_dump()函数用于输出变量的相关信息sha1($str)

sha1()函数用于计算字符串的SHA-1散列

这题的意思与上面提到的md5()函数类似,通过GET方式传递name参数与password参数,要求他们的值不能相等但是通过sha1()函数转化后的散列值要相等,相同地可以通过数组绕过,构造payload?name[]=123&password[]=

即可得到flag

419c557c32705a15f1b916dc5d3f6fc7.png

md5加密相等绕过

题目地址:http://123.206.87.240:9009/13.php

题目源代码如下:

5e94355153310546d9b75d8365b28888.png

这道题涉及到一个概念:php中的md5中0e的比较

如果md的值是以0e开头的,那么就与其他的0e开头的Md5值是相等的。例子如下:

md5(‘s878926199a’)=0e545993274517709034328855841020

md5(‘s155964671a’)=0e342768416822451524974117254469

可以看到两者的md5值都是以0e开头的,也就是说

md5(‘s878926199a’)==md5(‘s155964671a’)

因为代码中($a != ‘QNKCDZO’ && $md51 == $md52)

发现 $a != ‘QNKCDZO’ 并且 $md51 == $md52

因为$md51 = md5(‘QNKCDZO’)=0e830400451993494058024219903391

根据上文介绍,我们发现只要满足md5加密后为 “0e***************”就可以,下面给出几个0e开头的md5和原值s878926199a

0e545993274517709034328855841020

s155964671a

0e342768416822451524974117254469

s214587387a

0e848240448830537924465865611904

s214587387a

0e848240448830537924465865611904

s878926199a

0e545993274517709034328855841020

s1091221200a

0e940624217856561557816327384675

任意选取一个,构造payload?a=s878926199a

即可得到flag

03e28621c5de91397884d03d7d6f8b80.png

十六进制与数字比较

题目地址:http://123.206.87.240:9009/20.php

题目源码如下:

1ad311712e69f37855204249fee40225.png

分析代码,可知题目的意思是通过GET方式传递password参数,当password的值与3735929054相等时输出flag,但是代码规定了password不能以数字的形式出现,于是将3735929054转换成十六进制数

01b0eaaae88686b6fb8889f82775e2b9.png

注意要在转换结果前加上0x,表示为十六进制数,构造payload?password=0xdeadc0de

即可得到flag

960fdf1b5d7c368dd5aa4800070d3318.png

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 be alphanumeric

';

}

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

';

}

}

?>

题目出现的函数在数组返回NULL那道题已有解释,对于这道题的意思就是说通过GET方式传递一个password参数,password只能是数字或者大小写英文字母,password的长度必须要小于8或大于9999999且password中要有”-“。

同样的这题可以用数组绕过strpos()函数,构造payload?password[]=2

可得到flag

4a812dcaa29312598cb3900f2ed45827.png

还可以通过%00截断绕过ereg()函数,构造payload?password=1e9%00*-*

也可以得到flag

ba814e8d6448c8f7b722e1755c2234b3.png

strpos数组绕过

题目地址:http://123.206.87.240:9009/15.php

题目源码如下:

6434bc6fc461c6a384441ee2cd6627e4.png

题目和上面的题目差不多,意思就是用GET方式传递ctf参数,ctf必须为数字且其内容要有#biubiubiu,同样的可以用数组绕过strpos()函数,构造payload?ctf[]=2

即可得到flag

fbd44e12786b55621558c4ca1fb72450.png

数字验证正则绕过

题目地址:http://123.206.87.240:9009/21.php

题目源码:

ceb442bc8de8ac82879ec1a403f43b52.png

分析题目代码,可以发现几个地方需要重点注意

*

d2a592c93870b163c88877fb613ac6cb.png

这里需要一个正则表达式匹配,其中 [: graph:] 表示任意一个可打印字符。也就是说,要求$password长度大于12即可;

在本地测试 ’42aaaaaaaaaa’ 可以绕过bb57f5652fc4e68cb495bfd315789724.png

这里要求$password中必须包含标点符号、数字、大写字母、小写字母等,并且被检测到6次以上才能绕过

这里说明一下preg_match_all函数的用法:preg_match_all是全局匹配,每匹配成功一次就加1,一直匹配到字符串结束,最后返回匹配成功的次数;

在本题中,只要匹配到一个标点符号、或者匹配到一个数字、或者一个大写字母、或者一个小写字母,即为匹配成功。

于是,我把payload改成了:’42aaAaa2;aaaa’29e526769c94d37722505f3272acb0da.png

这里$c的值就是你的字符串种包含的类型的数目;也就是说,只要满足你的$password中包含标点符号、数字、大写字母、小写字母中三种及以上的类型,即可绕过

7e0bf4d5a2c5c84cd72985b3fe7f3b12.png

这里是一个弱类型比较,如果前两位是数字42,后面是字母等其它字符,比较时会强制转化成数字,于是,’42aaAaa2;aaaa’ 完全可以用来绕过

分析过后,可以构造出paylaodpassword='42aaAaa2;aaaa'

用POST方式传参,可以得到flag

77bada6bc057a058c1fa48d46ad14714.png

b2bbf12b81085789b561283c0b59c896.png

(还有两道题网页打不开,可能是题崩了吧。。。)

后记

这几道代码审计都是非常基础级别的题目,需要记住的应该是其中涉及的函数的含义和绕过方式

相关