
첫 번째 문제를 번역기로 해석해 보니
입력 검증만으로는 충분하지 않습니다!!
두 가지 작업을 모두 수행하고 매개변수화된 쿼리를 사용하여 사용자로부터 받은 입력을 검증해야 합니다. StackOverflow에서는 입력 검증으로 충분하다는 답변을 많이 볼 수 있습니다. 하지만 검증이 중단되었고 애플리케이션에 SQL 인젝션이 있다는 것을 알기까지는 시간이 걸립니다.
왜 그것이 충분하지 않은지에 대한 좋은 글은 https://twitter.com/marcan42/status/1238004834806067200?s=21 에서 찾을 수 있습니다
이전 과제 중 하나를 반복해 보겠습니다. 개발자가 필터링을 통해 SQL 주입 가능성을 수정했는데, 이 접근 방식에서 약점을 발견할 수 있나요?
여기에서 수업 목표에 대해 읽어보세요.
요약
1. 단순히 사용자가 입력한 값을 검사하거나 필터링하는 것에 의존해서는 안 된다. 매개변수화된 질의(Parameterized Queries, 예: Prepared Statement) 사용과 입력값 검증을 동시에(Both) 수행해야 한다.
2. 스택오버플로우(StackOverflow) 같은 커뮤니티에서는 "입력값 검증만 잘하면 안전하다"라는 답변을 흔히 볼 수 있지만, 이는 위험한 생각이다.
3. "개발자가 필터링(특정 문자를 걸러내는 방식)을 통해 SQL 인젝션을 수정했는데, 이 방식의 약점은 무엇일까요?"


문제를 확인해 보니
이번 문제는 앞에서 이미 해결했던 문제와 동일한 공격을 시도하면 되는 거 같습니다.

Query : 123' union select userid, user_name, password, null, null, cookie, null from user_system_data--
전에 사용했던 방식대로 Union을 사용하여 공격을 시도해 봤지만 Using spaces is not allowed! 라는 메시지가 나왔습니다. 공백은 사용할 수 없다고 나오는데 이건 아마도 공백에 대한 필터링을 사용하여 공격을 방어하고 있는 거 같습니다. 그렇다면 다른 방법으로 공백을 표현할 방법이 있는지 알아봐야 할 거 같습니다.

Query : 123'/**/union/**/select/**/userid,user_name,password,null,null,cookie,null/**/from/**/user_system_data--
우리가 평소에 사용하던 --(한 줄 주석) 은 범위를 지정할 수 없었지만 /**/(멀티 라인 주석) 은 시작과 끝을 설정할 수 있어서 공백으로 사용할 수 있습니다. 이렇게 해서 기존의 방식에서 다른 방법으로 우회하여 해당 문제를 해결하였습니다.

두 번째 문제를 번역기로 해석해 보니
입력 검증만으로는 충분하지 않습니다!!
그래서 쿼리에 공백이 포함되지 않았는지 확인하려는 마지막 시도가 실패했고, 개발팀은 입력 검증만 수행하는 방향으로 나아갔습니다. 이번에는 어디에서 잘못되었는지 확인해 주실 수 있나요?
요약
1. 이전 단계의 방어(공백 막기)가 실패하자, 개발팀이 여전히 매개변수화된 질의를 쓰는 대신 "더 강력한 입력값 검증"만으로 문제를 해결하려다 또다시 실패한 상황


문제를 확인해 보니
이전 방식과 똑같이 Union을 사용하여 공격을 시도해 보라는 같습니다. 하지만 이번에는 문제에서도 나왔듯이 공백에 대한 방어가 되어있을 거 같습니다.

먼저 공백을 사용하여 공격을 시도했지만, 이번에는 공백, and, /, or 같은 SQL 키워드들이 허락되지 않는다고 나와 있습니다.

전에 사용했던 방식인 /**/(멀티 주석 라인) 방식을 사용했더니 이번에는 unexpected token: USERID 라는 메시지가 나오면서 공격이 실패하였습니다.

자세히 살펴보니 이번에는 실행된 질의가 나왔습니다. 그런데 실행된 질의문을 살펴보니 SELECT, FROM 절이 보이지 않습니다.

혹시 SELECT, FROM 절도 필터링을 한 것인지 확인하기 위해서 123select456from789 예시를 사용해서 결과가 123456789이면 입력에 대한 SELECT, FROM 절이 제외된다는 것을 알 수 있을 것이고 결과 또한 위에 사진처럼 예상대로 SELECT, FROM 절을 사용할 수 없다는 걸 알았습니다.

그렇다면 필터링되는 단어들을 사용하되 사용하고자 하는 단어 사이에 해당 필터링 단어를 넣으면 되지 않을까? 예를 들어보겠습니다. 우리가 사용하고자 하는 단어는 SELECT, FROM 절입니다. 그렇다면 사용하고자 하는 단어(SELECT) 사이에 필터링된 단어(SELECT)를 집어넣으면 위에 검증에서처럼 해당 부분만 삭제되고 공간이 자동으로 이어져서 우리가 원하는 단어를 만들 수 있을 거 같습니다.
ex) SELselectECT -> select, from 필터링 -> SELECT
해당 방법을 이용하여 공격을 시도하였습니다. 위에 사진처럼 전에는 SELECT, FROM 절이 위치하고 있어야 할 자리가 비어 있었는데 이번에는 그대로 사용이 되고 있는걸 볼 수 있습니다. 이렇게 해서 기존에 필터링을 다시 한 번 더 우회해서 해당 문제를 해결할 수 있었습니다.

세 번째 문제를 번역기로 해석해 보니
이 과제에서 ORDER BY 필드를 통해 SQL 인젝션을 수행해 보세요. 전체 IP 주소가 너무 오래 걸릴 수 있으므로 마지막 부분을 알려드리겠습니다: xxx.130.219.202
참고: 이 할당의 제출 필드는 SQL 주입에 취약하지 않습니다.
요약
1. ORDER BY 필드에 취약점이 존재하니 공격해서 webgoat-prd 서버의 IP주소를 알아내라.

문제를 확인해 보니
위에 사진처럼 문제가 하나 있습니다. 문제에서도 알려줬듯이 제출 필드는 취약하지 않고 정렬 부분에 취약점이 존재하니 해당 부분에 대한 공격을 시도해 보겠습니다.

먼저 hostname을 기준으로 정렬하여 요청을 해보았습니다. SqlInjectionMitigations/servers 로 요청 보낼 때 column=hostname값을 넣어서 전달하는걸 볼 수 있습니다.

이번에는 ip를 기준으로 정렬하여 요청을 해보았습니다. 똑같이 column 파라미터에 ip값을 넣어서 전달하고 있습니다. 정렬 부분에 취약점이 있다고 하니 한번 공격을 시도해 보겠습니다.

파라미터값에 싱글 쿼터를 추가해서 공격을 시도해 봤습니다. 혹시 모를 오류를 확인하기 위해서입니다.

서버에서 오류를 보냈습니다. 확인해 보니 SQLSyntaxErrorException 즉, 해당 질의문에 문법이 맞지 않아서 생긴 오류입니다. 오류메시지를 자세히 살펴보니 select id, hostname, ip, mac, status, description from SERVERS where status <> 'out of order' order by ip' 정렬에 사용되는 질의가 그대로 노출되고 있습니다. 해당 질의를 해석해 보면 SERVERS 테이블에서 status가 'out of order' 아닌 것들을 찾아 ip를 기준으로, 오름차순으로 정렬하는 질의입니다. 그리고 파라미터에서 전달한 값이 ORDER BY 구문에 그대로 이어져서 있는 걸 보니 SQL injection이 가능하다고 생각할 수 있습니다.

조건문 : (case when(1<2) then hostname else ip end)
Blind SQL Injection을 하기 위해선 참/거짓을 판단할 수 있는 기준이 있어야 합니다. 해당 질의가 참일 때 응답을 확인해 보겠습니다. 공격 구문을 사용하기 위해서는 먼저 URL 인코딩을해야 공격을 할 수 있습니다.

hostname을 확인해 보면 오름차순으로 정렬된 게 보일 겁니다. 이번에는 거짓일 때는 ip로 정렬이 되는지 확인해 보겠습니다.

조건문 : (case when(1<2) then hostname else ip end)

ip를 기준으로 오름차순으로 정렬되었으니 여기서는 거짓조건이 잘 나온걸 확인할 수 있습니다.


조건문 : (case when(substring((select ip from servers where hostname='webgoat-prd'),1,1)='9') then hostname else ip end)
이제 공격을 시작할 겁니다. 위에 조건문을 해석하자면 hostname이 webgoat-prd를 찾아서 해당 ip값을 가져와 첫 번째 글자만 남겨서 9(비교 값)와 비교하도록 하여 맞는 값을 찾습니다. 여기서는 조건문을 보여드리기 위해 9라고 썼지만 9(비교 값)는 공격에 사용할 값이기 때문에 Intruder를 사용해서 9(비교 값)를 0~9로 값이 변경되어 webgoat-prd의 ip첫 번째 자릿값과 비교하면서 값을 알아냅니다.


페이로드가 1일 때 응답을 확인해 보면 hostname값으로 정렬된 걸 보니 해당 요청에 대한 응답이 참이므로 첫 번째 자리는 1xx.130.219.202 입니다.


두 번째 페이로드는 0일 때 hostname으로 정렬된 거로 보아 해당 요청에 대한 응답이 참이므로 두 번째 자리는 10x.130.219.202 입니다.


마지막 페이로드는 4일 때 hostname 정렬이므로 세 번째 자리는 104.130.219.202 입니다.

이렇게 해서 webgoat-prd의 ip주소 104.130.219.202를 입력하면 문제를 해결할 수 있습니다.
이번 실습에서는 필터링에 대한 여러 우회 방법과 ORDER BY 구문에서 발생할 수 있는 SQL Injection 공격을 알아보았습니다.