겉바속촉

[보안] Python SQL Injection 본문

IT 일기 (상반기)/네트워크 및 시스템 보안

[보안] Python SQL Injection

겉바속촉 2021. 2. 11. 01:59
728x90
반응형

 

 

이번에는 배워도 배워도 어려운 SQL Injection을 실습하려고 합니다.

python 코드를 이용해서 해보도록 할게요!!

 

 

@Attacker 가상머신

 

1. 우선 gedit이 없기 때문에 설치해주도록 하겠습니다.

 

┌──(kali㉿kali)-[~]
└─$ sudo apt-get update  
┌──(kali㉿kali)-[~]
└─$ sudo apt-get install gedit   

 

 

 

2. 파이썬파일을 사용할 것이기 때문에 또 설치해줄게요.

 

┌──(kali㉿kali)-[~]
└─$ sudo apt-get install python3-pymysql  

 

 

 

 

3. search.py 파일을 작성해보도록 할게요.

 

┌──(kali㉿kali)-[~]
└─$ sudo gedit search.py

 

search.py를 다음과 같이 작성했습니다.

import sys
import pymysql

# 파라미터 개수를 체크
if len(sys.argv) != 2:
    print("Invalid Parameter")
    sys.exit(1)

# DB 연결
conn = pymysql.connect(
    host = 'bee-box', 
    user = 'root', 
    password='bug', 
    port = 3306, 
    db = 'bWAPP', 
    charset = 'utf8')    

# (1) 파라미터로 전달된 검색어를 변수에 할당
search_name = sys.argv[1]

cursor = conn.cursor()

# (2) 쿼리문을 생성
sql_query = "select * from movies where title like '%" + search_name + "%'"

# (3) 쿼리문을 실행
cursor.execute(sql_query)
result = cursor.fetchall()

strFormat = '%-20s%-40s%-20s%-20s%-20s%-20s%-20s\n'
strOut = strFormat % ('ID', "title", 'release_year', 'genre', 'main_character', 'imdb', 'tickets_stock')

for row_data in result:
    strOut += strFormat % (row_data[0], row_data[1], row_data[2], row_data[3], row_data[4], row_data[5], row_data[6])

print(strOut)

cursor.close()
conn.close()

 

 

 

 

4. db 조회해보기

 

 

man을 입력해서 db를 조회하겠습니다.

 

⇒ select * from movies where title like '%man%'  

⇒ 원래 의도했던 형태로 쿼리가 실행될 겁니다.

┌──(kali㉿kali)-[~]
└─$ python3 search.py man            
ID                  title                                   release_year        genre               main_character      imdb                tickets_stock       
2                   Iron Man                                2008                action              Tony Stark          tt0371746           53                  
3                   Man of Steel                            2013                action              Clark Kent          tt0770828           78                  
5                   The Amazing Spider-Man                  2012                action              Peter Parker        tt0948470           13      

 

 

 

 

하지만 비정상적인 것을 입력해버리면 문제점이 생기지 않을까요???

 

 

"man' or 'a' = 'a' -- "을 입력해서 db를 조회하겠습니다.

 

⇒ select * from movies where title like '%man' or 'a' = 'a' -- %'

⇒ 외부 입력값에 의해 쿼리문의 형태가 변경되어서 실행이 되버립니다.

보이면 안될 데이터들까지 모두 값이 나오고 있습니다.

⇒ 이게 바로 SQL Injeciton 이죠!!

┌──(kali㉿kali)-[~]
└─$ python3 search.py "man' or 'a' = 'a' -- "                                                1 ⨯
ID                  title                                   release_year        genre               main_character      imdb                tickets_stock       
1                   G.I. Joe: Retaliation                   2013                action              Cobra Commander     tt1583421           100                 
2                   Iron Man                                2008                action              Tony Stark          tt0371746           53                  
3                   Man of Steel                            2013                action              Clark Kent          tt0770828           78                  
4                   Terminator Salvation                    2009                sci-fi              John Connor         tt0438488           100                 
5                   The Amazing Spider-Man                  2012                action              Peter Parker        tt0948470           13                  
6                   The Cabin in the Woods                  2011                horror              Some zombies        tt1259521           666                 
7                   The Dark Knight Rises                   2012                action              Bruce Wayne         tt1345836           3                   
8                   The Fast and the Furious                2001                action              Brian O'Connor      tt0232500           40                  
9                   The Incredible Hulk                     2008                action              Bruce Banner        tt0800080           23                  
10                  World War Z                             2013                horror              Gerry Lane          tt0816711           0         

 

 

 

 

5. 안전한 형태로 코드를 수정해보겠습니다.

 

┌──(kali㉿kali)-[~]
└─$ sudo gedit search.py  

 

search.py 파일의 쿼리문을 수정해야겠죠??

다음과 같이 고쳐주세요!!

 

쿼리문이 실행되는 부분도 다음과 같이 고쳐줄게요.

 

          :

 sql_query = "select * from movies where title like %s"    # 쿼리문의 구조를 정의


 cursor.execute(sql_query, ('%'+search_name+'%', ))        # 정의된 쿼리를 실행할 때 변수를 바인딩

           :                                                               # 입력값에 포함된 쿼리 조작 문자열을 이스케이프 처리

 

 

 

 

6. 이제 위에서 내려주었던 명령을 다시 내려볼게요.

┌──(kali㉿kali)-[~]
└─$ python3 search.py man                       
ID                  title                                   release_year        genre               main_character      imdb                tickets_stock       
2                   Iron Man                                2008                action              Tony Stark          tt0371746           53                  
3                   Man of Steel                            2013                action              Clark Kent          tt0770828           78                  
5                   The Amazing Spider-Man                  2012                action              Peter Parker        tt0948470           13                  

                                                                                                 
┌──(kali㉿kali)-[~]
└─$ python3 search.py "man' or 'a' = 'a' -- "   
ID                  title                                   release_year        genre               main_character      imdb                tickets_stock  

 

 

man을 입력해서 조회를 하면 아까와 동일하게 결과 출력되는 것을 알 수 있죠.

 

┌──(kali㉿kali)-[~]

└─$ python3 search.py man 

 

⇒ select * from movies where title '%man%'

 

 

 

 

하지만 아까처럼 전체 데이터를 출력해버리는 명령을 내려도 통하지 않습니다.

 

┌──(kali㉿kali)-[~]

└─$ python3 search.py "man' or 'a' = 'a' -- "

 

⇒ select * from movies where title '%man\' or \'a\' = \'a\' \-\- %' 

⇒ man이 아닌 다른 문자열을 넣어버리면 escape처리를 해버려서 데이터가 조회되지 않습니다.

⇒ 즉, data로 인식을 해버리고 쿼리로 인식을 하지 않습니다.

 

 

 

 

 


 

 

SELECT admin FROM users WHERE username = '입력값' 형태의 쿼리를 생성 시

 

1. 파이썬에서 SQL Injection이 발생할 수 있는 코드 유형

cursor.execute("SELECT admin FROM users WHERE username = '" + username + "'")

cursor.execute("SELECT admin FROM users WHERE username = '%s'" % username)

cursor.execute("SELECT admin FROM users WHERE username = '{}'".format(username))

cursor.execute(f"SELECT admin FROM users WHERE username = '{username}'")

 

 

궁금하기 때문에 search.py 파일 맨 아랫부분에 코드를 다음과 같이 추가해봤습니다.

 

그리고 다시 실행시켜보겠습니다.

man을 입력하면 쿼리가 정상적으로 나오지만

다른 것을 더 입력해주었더니 쿼리가 변형된 것을 알 수 있죠??

 

 

 

 

2. 파이썬에서 SQL Injection에 안전한 코드 유형

cursor.execute("SELECT admin FROM users WHERE username = %s", (username, ))

cursor.execute("SELECT admin FROM users WHERE username = %(uname)", {'uname': username})

 

 

 

 


 

 

 

@bee-box 가상머신에서  살펴볼 부분들 참고!!!

 

bee@bee-box:~$ sudo gedit /var/www/bWAPP/sqli_1.php

 

<?php

include("security.php");
include("security_level_check.php");
include("selections.php");
include("functions_external.php");		// security level 별로 실행될 함수 또는 파일을 정의
include("connect.php");			// DB 연결 정보

function sqli($data)				// Security level에 따라서 랩 적용할 함수를 판단
{										           
    switch($_COOKIE["security_level"])
    {
        case "0" :
            $data = no_check($data);		// no_check, sqli_check_1, ... 함수는 functions_external.php에 정의
            break;

        case "1" :
            $data = sqli_check_1($data);
            break;

        case "2" :
            $data = sqli_check_2($data);
            break;

        default :
            $data = no_check($data);
            break;
    }

    return $data;
}
?>
<!DOCTYPE html>
<html>
<head>
	:
</head>
<body>
	:
    <form action="<?php echo($_SERVER["SCRIPT_NAME"]); ?>" method="GET">
        <p>

        <label for="title">Search for a movie:</label>
        <input type="text" id="title" name="title" size="25">

        <button type="submit" name="action" value="search">Search</button>

        </p>
    </form>

<?php

if(isset($_GET["title"]))
{
	// (1) 파라미터로 전달된 값
    $title = $_GET["title"];		
	// (2) 쿼리문을 생성하는데 사용
	//     sqli() 함수는 보안 레벨에 따라서 쿼리 조작 문자열을 처리
    $sql = "SELECT * FROM movies WHERE title LIKE '%" . sqli($title) . "%'";	
	// (3) 쿼리문을 실행
    $recordset = mysql_query($sql, $link);
    if(!$recordset)
    {
?>
        <tr height="50">
			:
        </tr>
<?php
    }

    if(mysql_num_rows($recordset) != 0)
    {
        while($row = mysql_fetch_array($recordset))         
        {
?>
        <tr height="30">
            <td><?php echo $row["title"]; ?></td>
            <td align="center"><?php echo $row["release_year"]; ?></td>
            <td><?php echo $row["main_character"]; ?></td>
            <td align="center"><?php echo $row["genre"]; ?></td>
            <td align="center"><a href="http://www.imdb.com/title/<?php echo $row["imdb"]; ?>" target="_blank">Link</a></td>
        </tr>
<?php
        }
    }
    else
    {
?>
        <tr height="30">
			:
        </tr>
<?php
    }
    mysql_close($link);
}
else
{
?>
        <tr height="30">
			:
        </tr>
<?php
}
?>
    </table>
</div>
	:
</body>
</html>

 

 

 

그리고 위에서 쓰인 함수부분들이 궁금하다면 다음 경로로 가서 확인해주기!!!

 

bee@bee-box:~$ sudo gedit /var/www/bWAPP/functions_external.php

 

	:
function no_check($data)
{    
    return $data;
}

function sqli_check_1($data)
{
    return addslashes($data);
}

function sqli_check_2($data)
{
    return mysql_real_escape_string($data);
}
	:

 

 

 

728x90
반응형