SQL注入

thinkphp基本没得SQL注入,除非魔改

ORM框架的错误使用

一个专门用来防御SQL注入的框架

错误写法-java/mybatis

<select id = "findUserByname" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User">
<!--拼接mysql,引起SQL注入-->
SELECT * FROM table WHERE username = '${username}'
</select> @Test
public void testFindUserByName() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建UserMapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用userMapper的方法
list<User> list = userMapper.findUserByName("fuckdada' and 1=1#");
sqlSession.close();
System.out.println(list);
}

分析:

SELECT * FROM table WHERE username = '${username}'
如果使用'#{value}'绑定对象,没有漏洞,#{value}会采用预编译的形式针对value进行绑定;会被替换成一个问号(?),并且有参数映射,在调用的时候,会明确?是一个变量,会返回java中找变量的输入("fuckdada' and 1=1#"),这就是预编译。
能够有效防止SQL 如果用${value},这就是一个拼接了。就能够试试注入

错误写法-python/flask/sqlalchemy模块

flask快速入门唯一一点小问题就是,最好用mysql,方便点

mysql增删改查

预编译快速入门

环境:python3+mysql(sqli-labs靶场的security数据库)

@app.route("/",methods=["GET"])
def test():
username = request.args.get("username")
res = db.session.query(table).filter("username='{}'".format(username))

分析:

db.session(table).fileter("username={}".format(username))
filter是支持预编译的,但是如果采用format进行填充数据,将会形成sql注入,
可以理解为:采用format直接填充数据之后就成了拼接 为什么说没有经过预编译呢?
因为,在执行中,先"username={}".format(username)进行了拼接,在给orm进行预编译,注入发生在编译之前

正确写法:

@app.route("/",methods=["GET"])
def test():
username = request.args.get("username")
res = db.session.query(table).filter(table.username == username))

然后花了一亿点时间写了个代码(我是真的没想到需要用单音号进行一个闭合呀)

sql_injection.py

from flask import Flask,request
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine,text
from models import Users app = Flask(__name__)
engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/security", max_overflow=0, pool_size=5,echo = True)
Connection = sessionmaker(bind=engine)
con = Connection() # ############# 执行ORM操作:查看 #############
@app.route("/inject",methods = ["POST"])
def inject():
# username = request.form.get("username")
username = request.form.get("username")
print("[*] input username: " + username)
# users_query = con.query(Users).filter(Users.username==username) # safe
# users_query = con.query(Users).filter(Users.username == "{}".format(username)) #safe
users_query = con.query(Users).filter(text("Users.username='{}'".format(username)))
users = users_query.all() #报错注入 if users:
for item in users:
print(item.username)
return users[0].password
return "not found" if __name__ == "__main__":
app.run()

modles.py(和上一个代码同级就行,虽然这里累赘了,就这吧累了)

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column,Integer,String,Text,ForeignKey,DateTime,UniqueConstraint,Index Base = declarative_base() class Users(Base):
__tablename__ = 'users'#database name
id = Column(Integer,primary_key=True)
username = Column(String(32),index=True,nullable=False) # name colume,Index,not null
password = Column(String(32),nullable = False) def set_data(self,username="",password=""):
self.username=username
self.password=password # def set_data(self,username="",password=""):
# self.id=id
# self.username=username
# self.password=password def init_db():
"""
根据类创建数据表中
return
"""
engine = create_engine(
"mysql+pymysql://root:123456@127.0.0.1:3306/security?charset=utf8",
max_overflow = 0, # 超过连接池大小外最多创建的连接
pool_size=5 ,# 连接池大小
pool_timeout = 30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1, # 多久之后对线程池中的线程进行一次连接的回收(重置)\
)
Base.metadata.create_all(engine) def drop_db():
"""
根据类删除数据库表
:return:
"""
engine = create_engine(
"mysql+pymysql://root:123456@127.0.0.1:3306/security?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
) Base.metadata.drop_all(engine) if __name__ == '__main__':
# drop_db()
init_db()

接下来调试代码

有注入的

这里是username='-secure'#',这里是进行的拼接;不过当前的代码来看要是查不出user对象,这里是不会回结果的,而且对返回结果进行了指定。。。。

既然说了能注入,那就肯定能够注入,于是考虑使用布尔注入:注入如下

预编译的

SQL注入&预编译-奇安信攻防社区(写得真的好)

能够防止SQL注入;

这里在python中试了试,貌似不能够用%df;500了

SQL注入是在注入什么?

注入类型差异

核心思维:

SQL注入,就是执行一段SQL语句,关键在于SQL数据库类型

编程语言:

不同编程语言最终的目的都是为了将payload送入数据库层进行执行,能够看到注入点即可,语言不重要

产生注入的输入点

输入点决定了用什么样的Vector,以及是否需要绕过

SQL注入结构

一般来说,一个普通的SQL注入结构如下

action:select
object:table
subject(target):*
condition:
key:usersname
value:$username// user input

如果能够执行执行堆叠注入(是否能够加分号;),那么能够跳出action:select,执行更多的操作如,action:delete action:update

爆破数据的速度

报错 = 联合注入>外带数据>布尔盲注>时间盲注

为什么把报错放在第一位?

实际环境中,联合注入不一定能够构造出来,报错注入范围更加广阔,某些情况下,报错比联合快

输入点很多

不同的输入点有不同注入方式,代表着能不能绕过框架什么的,例如在预编译中,order之后能绕鱼变意思

可能进行SQL注入

宽字节注⼊

们知道字节是计算机存储世界中最⼩的衡量单位,1Byte = 8bits。所以⼀个字节最⼤能够表
示2^8=256个字符。
所以:
对ascii编码⽽⾔,⼀个字符⽤⼀个字节就可以表示,所以ascii编码最多可以表示256个字
符。
对GBK编码⽽⾔,⼀个汉字字符需要⽤两个字节表示。所以gbk编码理论上最多可以表示
256*256个字符。

案例:看SQLlabs的宽字节就行

<?php
$db = init_db();
$db->query("set SET NAMES 'gbk'); //设置gbk字符集
$username = addslashes($_GET['username']); //input: fuckdada' and 1=1#
$db->query("select * from table where username = '$username'");
?>

编码规范

要用%df吃到后面的字符,需要看编码规范

例如:要吃掉第二字节,需要用第一字节范围中的给出的才能吃掉

Hsql

Hibernate是⼀种ORM框架,⽤来映射与tables相关的类定义(代码),并包含⼀些⾼级特 性,包括缓存以及继承,通常在Java与.NET中使⽤(可参考NHibernate),但在Java⽣态系 统中更受欢迎。近来似乎逐渐有被Mybatis取代的趋势。

与Mybatis不同的是,Hibernate虽然也⽤了xml作为映射模型。但是他构成了⼀套⾃⼰的解析 引擎和语法,也就是HQL

https://www.cnblogs.com/chenssy/archive/2012/07/17/2594919.html
https://blog.51cto.com/jiaojusuimu/1881287

用户的输⼊作为HQL的⼀部分,先经过Hibernate解析引擎,渲染成sql语句,然后再进⼊到 数据库层进⾏查询。

所以不仅需要满足HQL的规范,同样需要满足渲染之后SQL的语法规范;

目前一般不考虑sql能够完全执⾏成功,⽽是利⽤sql报错注⼊+框架开启报错(因为java很多框架中都开启了报错),将有⽤的数据直接在错误回显中爆出来;

https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/hqlinjection-exploitation-in-mysql/
https://blog.sonarsource.com/exploiting-hibernate-injections/
https://www.sec-in.com/article/144
https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20In
jection/HQL%20Injection.md
https://conference.hitb.org/hitbsecconf2016ams/materials/D2T2%20-
%20Mikhail%20Egorov%20and%20Sergey%20Soldatov%20-
%20New%20Methods%20for%20Exploiting%20ORM%20Injections%20in%20Java%20App
lications.pdf

预编译之下的注入(复现失败)

php预编译——宽字节绕过

<?php
$username = $_GET['username'];
$db = "mysql:host=127.0.0.1;dbname=test;charset=gbk";
$dbname = "root";
$passwd = "root";
$conn = new PDO($dbs, $dbname, $passwd);
$conn->query('SET NAMES GBK');
$stmt = $conn->prepare("select * from table where username =
:username");
$stmt->bindParam(":username",$username);
$stmt->execute();
?

开始日志:(默认没开启,网上找一个一堆开慢查询的....)(51才是nb的)

但就查看的日志而言,貌似%df吃不了。。。(要是师傅们测试能过,能给俺教学一手吗?)

无法预编译的点

like后

这⾥再展开讲讲那些没办法⽤orm(没办法预编译)去保护的输⼊点,并且这种是通例,也就 是⽆视语⾔的。

在sql语句的模糊查找⾥⾯⽤的关键字like,⽽like关键字默认是不会预编译的(如果使⽤ Mybatis则是预编译报错)。数据库⽅给出的原因好像是like预编译会造成慢查询和DOS。

yu6.php
<?php
$username = $_POST['username']; // 接收username
# 建立数据库连接
$dbs = "mysql:host=127.0.0.1;dbname=security";
$dbname = "root";
$passwd = "123456";
// 创建连接,选择数据库,检测连接
try{
$conn = new PDO($dbs, $dbname, $passwd);
echo "Sucussful<br/>";
}
catch (PDOException $e){
die ("Error!: " . $e->getMessage() . "<br/>");
}
# 预编译语句
$conn -> setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// $stmt = $conn->prepare("select * from users where username like '%:username%'"); #无预编译
$stmt = $conn->prepare("select * from users where username like concat('%',:username,'%')"); #生效
$stmt->bindParam(":username",$username);
$stmt->execute();
$row = $stmt->fetch();
print_r($stmt);
print_r($row);
// print("asdf");
$conn=null; # 关闭链接
?>

结果:

这里指的无法预编译,就直接连'%username%'都没变。

(麻了,我突然发现,为什么我昨天测试python的时候不直接开日志呢。。。。找了半天在命令行输出语句。。。。)

当然,改一下语句,用concat拼接能够执行预编译

不能加引号的地方无法预编译

在执行结果日志中看到,执行预编译的地方都会被加双引号

预编译步骤:
1. 执行 addslashes($data),为特殊字符转义
2. 强制加上单引号进行拼接,select * from users where username = '${data}'

由于会强制加上双引号,但是在执行原生SQL的时候,有的地方加不得,例如表名、列名、limit⼦句、order by[desc/asc];

例如给表名加上单引号,会被当做字符串进行输出

例如给列名加上单引号,会报错

所以当程序在特殊位置不能加预编译,就可以考虑一手注入

虽然可以人为加过滤,但是好歹绕了预编译;

最新文章

  1. 4.Java网络编程之TCP/UDP
  2. android: 服务的生命周期
  3. [玩转微信平台]XML的格式化- 如何去掉XML 文档头和命名空间
  4. 手机远程调试工具spy-debugger
  5. Java中Array的常用方法
  6. The authenticity of host 192.168.0.xxx can&#39;t be established.
  7. IOS开发UI篇之自动滚动图片
  8. DllImport的具体用法
  9. WordPress 后台禁用Google Open Sans字体,加速网站
  10. linux 和unix 的区别
  11. 读书笔记:php_tizag_tutorial
  12. Swift初体验(两)
  13. UILabel设定行间距方法
  14. ionic1 打包过程 常用命令行
  15. 在deepin 15.5中安装vs code并配置c/c++环境
  16. Ubuntu12.04 LTS 32位 安装ns-2.35
  17. dhcp、tftp及pxe简介
  18. application/x-www-form-urlencoded 的contentType,POST数据内容过大,导致tomcat的request取不到参数
  19. 洛谷 3706 [SDOI2017]硬币游戏——思路
  20. python并行计算(持续更新)

热门文章

  1. 如何禁止win7自动锁屏
  2. 第一章:seaborn图形美学
  3. CompletionService 使用小结
  4. 【Java SE进阶】Day10 缓冲流、转换流、序列化流 、打印流
  5. 【离线数仓】Day03-系统业务数据仓库:数仓表概念、搭建、数据导入、数据可视化、Azkaban全调度、拉链表的使用
  6. 如何查看计算机的CPU信息
  7. java中加号的用法
  8. Django框架:10、Ajax补充说明、多对多三种创建方法、Django内置序列化组件、批量操作数据方法、分页器思路、form组件
  9. 论文解读(CAN)《Contrastive Adaptation Network for Unsupervised Domain Adaptation》
  10. cmd窗口中java命令报错。错误:找不到或无法加载主类 java的jdk安装过程中踩过的坑