겉바속촉
[보안] Python SQL Injection 본문
이번에는 배워도 배워도 어려운 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);
}
:
'IT 일기 (상반기) > 네트워크 및 시스템 보안' 카테고리의 다른 글
[보안] 웹 도큐먼트 루트 (0) | 2021.02.11 |
---|---|
[보안] 파일 업로드 취약점 (1) | 2021.02.11 |
[보안] 접근제어 (feat. 데이터레벨의 접근통제) (0) | 2021.02.10 |
[보안] 접근제어 (feat. 기능레벨의 접근통제) (0) | 2021.02.08 |
[보안] Bee-Box 설치 및 실행 (0) | 2021.02.08 |