Java審計之SQL注入篇
Java審計之SQL注入篇
0x00 前言
本篇文章作為Java Web 審計的一個入門文,也是我的第一篇審計文,後面打算更新一個小系列,來記錄一下我的審計學習的成長。
0x01 JDBC 注入分析
在Java裡面常見的資料庫連接方式也就那麼幾個,分別是JDBC,Mybatis,和Hibernate。
注入常見場景分析
JDBC的連接是比較繁瑣的,並且是最原始的連接方式,我們來看看JDBC的最原始的連接程式碼
Get型注入:
@WebServlet("/demo")
public class domain extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get訪問");
String id = req.getParameter("id");
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/demo", "root", "root");
String sql = "select * from users where id = '"+id+"' ";
Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
這裡編寫了一個serlvet獲取get的值,連接資料庫使用了jdbc的方式進行連接,採用了拼接的方式直接拼接到了sql語句裡面去。這樣的程式碼如果在傳入前沒做過濾直接拼接,就會產生sql注入。
在實際運用當中如果不採用框架使用JDBC的方式,普遍會編寫一個工具類來完成這些繁瑣的配置,但是具體的實現還是調用這些方法來進行實現,只是進行了一個簡單的封裝。
在程式碼審計的時候,如果看到是JDBC的方式進行連接可以跟蹤一下他的程式碼,看他有沒有調用自己定義的過濾方法,如果沒有的話,就會存在sql注入,當然這是在未使用預編譯的情況下。
後面的重複的比較多,都是大致相同,我後面就貼出一些主要的程式碼進行分析。
POST型注入:
String sql = "select * from users where username = '"+username+"' and password = '"+password+"' ";
GET的注入和POST的其實差不多,只是獲取值的地方不一樣。
Like型注入:
String name = req.getParameter("name");
String sql = "select * from users where name like '%'+name+'%'";
Header注入:
String referer = req.getHeader("referer");
String sql = "update user set referer ='"+referer+"'";
以上列了幾種方式都是JDBC採用拼接的方式造成SQL注入的程式碼。
JDBC 預編譯
預編譯的定義其實就是使用問號先來佔位,後面再傳入具體的值。
後面傳值的時候,程式會把傳入的參數,自動轉換為spring類型的字元,並不會拼接成sql語句生效。
Connection conn = JDBCUtils.getConnection();
String sql = "select * from users where username = ? and password = ?";
PreparedStatement pstmt = conn.prepareStatement(sql); //使用預編譯傳入sql語句
pstmt.setString(1,username); //設置第一個參數為username
pstmt.setString(2,password); //設置第二個參數為password
pstmt.executeQuery();
0x02 Mybatis 注入分析
Mybatis獲取值的方式有兩種,分別是${}
和 #{}
。
#{}:解析的是佔位符問號,可以防止SQL注入,使用了預編譯。
${}:直接獲取值
在Mybatis裡面一般會採用#{}
來進行取值,但是也會有特殊情況。
like注入:
我們這裡還是以程式碼做演示
select id="findlike" resultType="com.test.domain.User" parameterType="string">
select * from user where name like '%#{name}%',
</select>
我們在運行的時候會發現,程式碼直接就會拋出異常。
正確程式碼:
select id="findlike" resultType="com.test.domain.User" parameterType="string">
select * from user where name like '%${name}%',
</select>
需要使用${}的方式進行取值。
或者是
<select id="findlike" resultType="com.test.domain.User" parameterType="string">
select * from user where name like #{name}
</select>
測試類:
public void findlike(){
List<User> ming = userDao.findlike("'%'+xiao+'%'");
for (User user1 : ming) {
System.out.println(user1);
}
}
另外還有種寫法:
<select id="findlike" resultType="com.test.domain.User" parameterType="string">
select * from user where name like concat('%',#{name},'%')
</select>
這裡在前面進行加入兩個%
,再進行傳入這樣的方式也不會報錯,但是使用
#
直接拼接%
就會報錯。
like不能直接使用預編譯,如果在沒處理好參數的情況下進行傳入,也是會產生sql注入的。
in後注入
Select * from news where id in (#{id}),
也是拼接使用預編譯這樣的程式碼也會報錯。
正確寫法:
Select * from news where id in (${id})
order by 注入
Select * from news where title ='#{titlename}' order by #{time} asc
執行會報錯
正確寫法:
Select * from news where title ='#{titlename}' order by ${time} asc
0x03 CMS 審計
測試環境
IDEA :2020.1.2 X64
MYSQL :5.7.26
TomCat:8.0
JDK :1.8
搭建環境
下載源碼
//down.admin5.com/jsp/132874.html
。
idea中導入項目,添加pom.xml文件為maven文件。如果Spring註解報錯說明Spring的環境還沒拉去下來,刷新一下pom.xml文件就好了。
這裡配置是82埠,目錄就默認就行。
配置tomcat也設置為82埠
這裡要注意路徑需要根路徑,否則載入有一些css資源的時候路徑會因為路徑問題載入不少。
這樣就配置完成了,但是還是會發現有一些get,set的方法會爆紅。
項目的說明文檔裡面給出了解決方法,只需要安裝一下lombok插件重啟一下就解決了,這裡是因為一些程式碼中並沒有實際編寫get和set的方法,使用的是插件去提供的。
這些完成後,就可以講提供好的sql文件導入進去。進行啟動
這些都是自己專門踩過的坑,一段操作猛如虎後,啟動完成。但是會有一些報錯,sql文件在導入的時候,有些執行錯誤了,幾張表沒創建成功,在進行操作該表的時候未找到該表,就報錯了。
將就一下把!
第一步肯定是先看他的web.xml的配置,看他都使用了哪些框架
確實該cms是一個使用了SSM框架,也就是Spring+Spring Mvc+Mybatis
(哈哈哈,其實是看說明文檔知道的。)
審計SQL 注入
文件的劃分很細,很清楚看到他的結構,點開dao文件下的任意文件,看看Mybatis是使用了註解開發還是配置文件開發。
點開沒發現有Mybatis的註解,那就肯定是使用了配置XML的方式。
映射文件會和dao的介面在同層目錄下。
直接就來找$
符號吧,看看哪些是直接調用了$
來進行取值並且沒經過過濾的。
發現deleteArticleByIds使用的是$
取值。
找到配置文件對應的dao介面
選中dao介面中deleteArticleByIds,Curl+左鍵可以看到哪些類調用了該方法。
我這裡就跳轉到了service層的一個實現類裡面。
主要關注service層程式碼,過濾處理的會從service層去實現。
並沒有發現過濾的程式碼
接下來就可以去找該service對應的Controller,這個可以使用idea的ctrl+Alt+H快捷鍵去查詢調用層次,去看Controller的位置。
查看到了Controller文件,先找他的目錄路徑
/admin/article
在搜索一下deleteArticleByIds具體在哪裡調用和出現,就得到了具體的漏洞位置。
漏洞位置:
//127.0.0.1:82/admin/article/delete
訪問漏洞位置
點擊刪除進行抓包
扔到sqlmap跑一下
參考文章
//mp.weixin.qq.com/s?__biz=MjM5OTk2MTMxOQ==&mid=2727827368&idx=1&sn=765d0835f0069b5145523c31e8229850&mpshare=1&scene=1&srcid=0926a6QC3pGbQ3Pznszb4n2q
//xz.aliyun.com/t/2646#toc-1
0x04 結尾
前面的環境配置了比較久,耽誤了不少時間。