继续把这个刷完
0x01 X-forwarded-For绕过指定IP地址
这里我觉得要好好地去补习一下HTTP协议
放上源代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function GetIP(){
if(!empty($_SERVER["HTTP_CLIENT_IP"]))
$cip = $_SERVER["HTTP_CLIENT_IP"];
else if(!empty($_SERVER["HTTP_X_FORWARDED_FOR"]))
$cip = $_SERVER["HTTP_X_FORWARDED_FOR"];
else if(!empty($_SERVER["REMOTE_ADDR"]))
$cip = $_SERVER["REMOTE_ADDR"];
else
$cip = "0.0.0.0";
return $cip;
}
$GetIPs = GetIP();
if ($GetIPs=="1.1.1.1"){
echo "Great! Key is *********";
}
else{
echo "错误!你的IP不在访问列表之内!";
}
函GetIP()这里有一个缺陷,关键代码如下: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
30else if(!empty($_SERVER["HTTP_X_FORWARDED_FOR"]))
$cip = $_SERVER
["HTTP_X_FORWARDED_FOR"];
if ($GetIPs=="1.1.1.1"){
echo "Great! Key is *********";
```
所以这里我们可以burp抓包构造X_FORWARDED_FOR=1.1.1.1,就可以绕过
这里补充一下关于X_FORWARDED_FOR的知识点:
X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。
# 0x02 intval()四舍五入绕过
源代码:
```php
if($_GET[id]) {
mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
mysql_select_db(SAE_MYSQL_DB);
$id = intval($_GET[id]);
$query = @mysql_fetch_array(mysql_query("select content from ctf2 where id='$id'"));
if ($_GET[id]==1024) {
echo "<p>no! try again</p>";
}
else{
echo($query[content]);
}
}
我们要查询id=1024的信息,但是传入1024会报错,所以因为intval()会将数字进行四舍五入,所以传入1024.1
0x03 数字验证正则绕过
源码: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
error_reporting(0);
$flag = 'flag{test}';
if ("POST" == $_SERVER['REQUEST_METHOD'])
{
$password = $_POST['password'];
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配
{
echo 'Wrong Format';
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;
}
}
这道题主要就是审计需要绕过的地方有哪些,一下是关键代码: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//关键代码一 password要在12个字符以上
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配
{
echo 'Wrong Format';
exit;
}
//关键代码二 匹配次数要大于6
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
//[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母
if (6 > preg_match_all($reg, $password, $arr))
break;
//关键代码三 数字符号字母大小写至少要有3种
$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;
题外话:函数preg_match_all() 正则匹配,返回匹配次数.可为0。
如下代码:1
2
3
4
5
6
7
8
9
10
11
12
13
$password = $_GET['password'];
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
$times=preg_match_all($reg, $password, $arr);
echo $times;
//输入password=1a11 返回3
//输入password=1a.1 返回4
所哟这道题最终要有42,还要有12个字符以上,还要有至少3种类型,匹配次数要在六次以上
想到科学计数法
所以最终payload为:1
2
342.00e+00000000000
或
420.000000000e-1
0x04 十六进制与数字绕过
源代码: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
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);
关键代码:1
2
3
4
5
6
7
8
9
10
11
12
13for ($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;
}
对我们输入的password进行遍历,其ASCII值是否是在1到9的范围内,目的要要遍历后的结果其ASCII值不在这个范围内还要输入的数字和’3735929054’相等。使用16进制可绕过。
payload为:1
?password=0xdeadc0de
0x05
源码:1
2
3
4
5
6
7
8
9
10
11
12
error_reporting(0);
$flag = "flag{test}";
$temp = $_GET['password'];
is_numeric($temp)?die("no numeric"):NULL;
if($temp>1336){
echo $flag;
}
利用php弱类型的一个特性,当一个整形和一个其他类型行比较的时候,会先把其他类型intval再比。如果输入一个’1337a’这样的字符串,在’is_numeric’中返回’true’,然后在比较时被转换成数字’1337’,这样就绕过判断输出’flag’。
payload:1
?password=1337a
0x06 数字md5()验证绕过
源代码:1
2
3
4
5
6
7
8
9
10
error_reporting(0);
$flag = 'flag{test}';
$temp = $_GET['password'];
if(md5($temp)==0){
echo $flag;
}
这里有两个解决方式
(1) null == 0 返回true
输入’?password=’
(2)科学计数法
经过MD5运算后,为0e**的形式,其结果为0*10的n次方,结果还是零1
2http://127.0.0.1/php_bug/23.php?password=240610708
http://127.0.0.1/php_bug/23.php?password=QNKCDZO
0x07 switch没有break
源代码: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
error_reporting(0);
if (isset($_GET['which']))
{
$which = $_GET['which'];
switch ($which)
{
case 0:
case 1:
case 2:
require_once $which.'.php';
echo $flag;
break;
default:
echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);
break;
}
}
```
这里先说一说switch的用法
```php
switch工作原理:
对表达式(通常是变量)进行一次计算
把表达式的值与结构中 case 的值进行比较
如果存在匹配,则执行与 case 关联的代码
代码执行后,break 语句阻止代码跳入下一个 case 中继续执行
如果没有 case 为真,则使用 default 语句
实例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$favfruit="orange";
switch ($favfruit) {
case "apple":
echo "Your favorite fruit is apple!";
break;
case "banana":
echo "Your favorite fruit is banana!";
break;
case "orange":
echo "Your favorite fruit is orange!";
break;
default:
echo "Your favorite fruit is neither apple, banana, or orange!";
}
然后我们会发现,实际上源码里是没有0和1的情况是没有break的….
0x08 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<!-- index.php -->
require_once('shield.php'); //包含shield.php,跟踪到shield.php
$x = new Shield();
isset($_GET['class']) && $g = $_GET['class'];
if (!empty($g)) {
$x = unserialize($g);
}
echo $x->readfile();
<img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
<!-- shield.php -->
//flag is in pctf.php
class Shield {
public $file;\
function __construct($filename = '') {
$this -> file = $filename;
}
function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
php反序列化问题,当使用函数unsearlize()被使用时,传入的字符串在未经过滤的情况下,将会存在漏洞问题。
关键代码:1
2
3
4if (!empty($g)) {
$x = unserialize($g);
}
echo $x->readfile();
传入参数为class,存入$g,进行反序列化处理,
由于:1
$x = new Shield();
跟踪至方法readfile():1
2
3
4
5function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
所以此处附上payload:1
?class=O:6:"shield":1{s:4:"file";s:8:"pctf.php";}