網站首頁 編程語言 正文
前言
我相信很多小伙伴在玩sql注入報錯注入時都會有一個疑問,為什么這么寫就會報錯?曾經我去查詢的時候,也沒有找到滿意的答案,時隔幾個月終于找到搞清楚原理,特此記錄,也希望后來的小伙伴能夠少走彎路
0x01
我們先來看一看現象,我這里有一個users表,里面有五條數據:
然后用我們的報錯語句查詢一下:
select count(*),(concat(floor(rand()*2),(select version())))x from users group by x
成功爆出了數據庫的版本號。
要理解這個錯誤產生的原因,我們首先要知道group by語句都做了什么。我們用一個studetn表來看一下:
現在我們通過年齡對這個表中的數據進行下分組:
形成了一個新的表是吧?你其實應該能夠想到group by 語句的執行流程了吧?最開始我們看到的這張sage-count()表應該時空的,但是在group by語句執行過程中,一行一行的去掃描原始表的sage字段,如果sage在sage-count()不存在,那么就將他插入,并置count()置1,如果sage在sage-count()表中已經存在,那么就在原來的count(*)基礎上加1,就這樣直到掃描完整個表,就得到我們看到的這個表了。
注:這里有特別重要的一點,group by后面的字段時虛擬表的主鍵,也就是說它是不能重復的,這是后面報錯成功的關鍵點,其實前面的報錯語句我們已經可以窺見點端倪了
0x02
正如我前面所說的,報錯的主要原因時虛擬表的主鍵重復了,那么我們就來看一下它到底是在哪里,什么時候重復的。這里rand()函數就登場了。
首先我們先來了解rand()函數的用法:
1.用來生成一個0~1的數
2.還可以給rand函數傳一個參數作為rand()的種子,然后rand函數會依據這個種子進行隨機生成。
那他們的區別是什么呢?我們來看一下,這兩個語句的執行效果:
可以看到rand()生成的數據毫無規律,而rand(0)生成的數據則有規律可循,是:
0110 0110
注:如果你覺得數據不夠,證明不了rand()的隨機性,你可以自己多插入幾條數據再查詢試一下。
0x03
現在我們弄清楚了group by語句的工作流程,以及rand()與rand(0)的區別,那么接下來就是重點了,mysql官方說,在執行group by語句的時候,group by語句后面的字段會被運算兩次。
**第一次:**我們之前不是說了會把group by后面的字段值拿到虛擬表中去對比嗎,在對比之前肯定要知道group by后面字段的值,所以第一次的運算就發生在這里。
**第二次:**現在假設我們下一次掃描的字段的值沒有在虛擬表中出現,也就是group by后面的字段的值在虛擬表中還不存在,那么我們就需要把它插入到虛擬表中,這里在插入時會進行第二次運算,由于rand函數存在一定的隨機性,所以第二次運算的結果可能與第一次運算的結果不一致,但是這個運算的結果可能在虛擬表中已經存在了,那么這時的插入必然導致錯誤!
所以我們現在通過一個例子來驗證我們的理論,拿出我們最開始的例子:
select count(*),(concat(floor(rand(0)*2),'@',(select version())))x from users group by x
聲明:users表就是本文第一個表,表中有五條數據
注意我這里用的是rand(0),不是rand(),rand(0)生成的有規律的序列:
我們跟著剛剛的思路走,最開始的虛擬表是空的,就像下面一樣:
count(*) | x |
---|---|
? | ? |
當我掃描原始表的第一項時,第一次計算,floor(rand(0)*2)是0,然后和數據庫的版本號(假設就是5.7.19)拼接,到虛擬表里去尋找x有沒有x的值是x@5.7.19的數據項,結果顯然是沒有,那么接下來就將它插入到上表中,但是還記得嗎,在插入之前會進行第二次計算,這時x的值就變成了1@5.7.19,所以虛擬表變成了下面這樣:
count(*) | x |
---|---|
1 | 1@5.7.19 |
現在掃描原始表的第二項,第一次計算x==’1@5.7.19‘,已經存在,不需要進行第二次計算,直接插入,得到下表:
count(*) | x |
---|---|
2 | 1@5.7.19 |
掃描原始表的第三項,第一次計算x==‘0@5.7.19’,虛擬表中找不到,那么進行第二次計算,這時x==‘1@5.7.19’,然后插入,但是插入的時候問題就發生了,虛擬表中已經存在以1@5.7.19為主鍵的數據項了,插入失敗,然后就報錯了!
上面是使用rand(0)的情況,rand(0)是比較穩定的,所以每次執行都可以報錯,但是如果使用rand()的話,因為它生成的序列是隨機的嘛,所以并不是每次執行都會報錯,下面是我的測試結果:
執行了五次,報錯兩次,所以是看運氣。
總結
總之,報錯注入,rand(0),floor(),group by缺一不可
原文鏈接:https://tntaxin.blog.csdn.net/article/details/80455884
相關推薦
- 2022-07-02 C#使用SharpZipLib壓縮解壓文件_C#教程
- 2022-08-18 Docker搭建私有GitLab服務的方法_docker
- 2023-05-06 C++?lambda函數詳解_C 語言
- 2022-06-20 詳解Python列表解析式的使用方法_python
- 2022-06-28 python反轉單鏈表算法題_python
- 2023-07-04 spring boot security驗證碼登錄示例
- 2022-05-14 聊聊python?邏輯運算及奇怪的返回值(not,and,or)問題_python
- 2022-08-15 BufferedInputStream與FileInputStream的區別
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支