이 문제 풀다가 여러 번 사경을 헤맸다..
두 번의 혼동이 있었다.
[ 혼동 1 ]
어찌보면 당연한건데, 다시 깨닫게되었다.
문자열 안으로 감싸져 있는 상태에는 문자들로 보기 때문에 주석문자들 또한 작동하지 않는다는 것
ex)
// /* */ 작동안함
String str = "hello/*"*/
// //도 마찬가지. 당연히 문자로 인식하게됨.
String str = "hello//"
다른 언어들도 마찬가지.
[ 혼동 2 ]
정리한 내용엔 틀린거 없는데, 애초에 질문부분이 잘못되어서 다 선 그어놓음.
그리고 이로인해 헷갈릴 수 있는게,
"데이터가 Url Encoding 되어 전송되고 서버에서 Url Decoding되어 데이터를 읽고 사용하는 구조에서, 입력한 값이 문자열 안의 영역으로 삽입되는 경우엔 이것이 url디코딩 되지 않고 url인코딩상태 그대로 쓰일까?, 즉 문자열 안의 영역으로 삽입되지 않아야 디코딩이 될까?" 이다.
답은 X. 이런 말 자체가 이상하다.
헷갈려해선 안된다. '문자열 안의 영역이다? 바깥영역이다?' 와 'url디코딩작업' 둘은 완전 분리된 이야기다.
GET 방식의 통신 예로, 데이터를 보내면 url 인코딩이 되어 전송이 되고, 서버에서 수신할 때 url 디코딩을 거쳐 데이터를 사용하게 되는 방식을 갖는다.
이게 문자열 안에 쓰이건 안쓰이건 간에 $_GET['input'] 은 이미 서버에서 url디코딩 해서 받은 값이라는 것이다.
그러므로, 문자열 안에 쓰인다고 해서 이것이 url디코딩 안된 url인코딩 상태이고 문자열 밖에 쓰일때만 이것이 url디코딩 상태라는 그런 구상은 불가능하다. 만약 ㅋㅋ 극단적으로 봤을 때 서버가 url디코딩 작업을 거치지 않는 서버라면, 저 값은 애초부터 어디에 쓰이던 그냥 url디코딩 안된상태인거고, url 디코딩 작업을 거쳤으면 마찬가지로 문자열 밖이건 안이건 똑같이 url디코딩 상태라는 것이다. 코드의 어디에서 쓰이건, url 디코딩이 된 상태면 어딜가나 디코딩이 된 상태고, 안된 상태면 안된 상태다(그런서버는 없겠지만..ㅋㅋ) 즉, 통일!
url 인코딩 / 디코딩은 url을 통한 데이터 송수신 과정에서의 변환일뿐이다.
GET 방식에서
$query = "select id from prob_succubus where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
동작이 위와 같을 떄,
내가 주소창에 ?id=%09hello 를 입력하면, 이건 탭을 뜻하는 문자의 인코딩값이므로, 디코딩 된 후
where id='(탭)hello' 로 들어간다. 이건 당연하다.
%09는 (탭)의 인코딩 값이므로, 수신할 때 다시 (탭)으로 디코딩 된다.
인코딩과 디코딩의 개념에 대해서 조금만 생각해봐도 이해가 바로 된다.
URL인코딩과 URL디코딩은 URL상에서 데이터를 송수신 하는 과정에서 몇 문자들을 약속된 방법으로 변환한 뒤에 통신하는 것이다.
그러므로 전송할 때 URL인코딩한 데이터를 읽어들일 때엔, 서버에서 URL디코딩을 거치고 데이터를 사용하는 구조를 갖는다.
문자열의 문자 처리 혼동으로 이걸 헷갈려하면 안된다. 문자열 처리와 이걸 연관지으려 하지 마라. 완전 별개의, 다른 차원의 작업이다.
참고로 SUCCUBUS 문제에서 아래 방법을 쓰려다가 갑자기 헷갈리게 되었었다.
주석을 이용한 SQL Injection
: '#'의 주석 범위는 1 line이다. 1 line을 나누는 기준은 %0a로 나뉘기 때문에 아래 예제와 같은 SQL Injection을 수행할 수 있다.
* select test1 from test where id='abc'# and pw='%0a or id='admin'%23
: /* */
* select test1 from test where id='abc'/* and pw=''*/ or id='admin'%23
[ 혼동 3 ]
또 한가지, 문제를 풀다가 어려웠던 점은 이거다.
나는 문제에서
id= ' ' and pw = ' ' 를 우회하기 위해 URL에 아래와 같은 값을 입력했다
?id=guest\&pw=or%201=1#
즉, 내가원했던 결과는
id = 'guest\' and pw = 'or 1=1#'
근데 이게 정답이 아닌 것이다.. 그래서 이런 저런 다양한 경우의 수를 대입해보다가 정말 도저히 안되겠어서... 도저히 모르겠어서.. 검색을 해서 다른 분의 글을 봤다. 근데 그분이 나랑 다른 점은 #을 인코딩된 %23으로 넘겨준 것 밖에 없었다. 내가 의도했던 방식이 맞았다는 것이다ㅜ... 근데 왜 #을 직접 URL에 넣어주면 안됐던 것일까? 다시 한번 넣어봤다.
근데 이번엔 자세히 결과를 보니 내가 입력한 샵이 출력에서 사라져있다.
query : select id from prob_succubus where id='guest\' and pw='or 1=1'
뭐지, 이번엔 id 부분에 #을 한번 넣어보고 url을 전달했다.
?id=12345#&pw=12345
근데 결과는 아래와 같았다.
query : select id from prob_succubus where id='12345' and pw=''
엥??!!!! 여기서부터 위의 [혼동 1] 에서 겨우 정리했던 게 다시 혼란스러워졌다.
1. #이 php에서 주석으로 들어갔나..? 그치만 문자열 안에 껴들어간 #이므로 주석의 기능을 못할텐데..??
그럼 문자열 사이에 들어간 #도 제기능을하는건가..? 근데 그렇다기엔 결과에 id ='12345' 이렇게 작은따옴표로 잘 감싸져있는데..?(끝마무리가 잘 되어있음. 주석이라면 '12345 하고 주석처리가 되어서 select 전체 문자열에 대한 닫음 표시도 없고 문법 에러가 나야한다.)
2. #이 sql에서 주석으로 들어갔나?..? 그치만 마찬가지로 문자열 안에 껴들어간 #이므로 주석의 기능을 못할텐데..?
정답은 여기서 #은 php나 sql에서의 주석역할로 들어간 것이 아니다. [혼동 1] 에서 정리한 내용이 맞다. 괜히 다시 잠깐 헷갈렸다.
문자열 안에 위치한 문자는 주석문자일지라도 문자 자체의 의미만 나타나게 되는게 맞다. 주석의 기능을 못한다. '12345#' 으로 들어갔으니, #은 주석이 아닌 문자열 안의 문자 #인 것 뿐이다.
그럼 이게 무엇일까... "혹시 #이 메타문자와 같은, url에서 쓰이기로 약속한 예약문자가 아닐까?" 라는 생각을 하고 알아보게 되었다.
결과는 그렇다였다. 정확한 역할은 찾아내지 못했는데 anchor 라는 역할을 하는 문자였다.
난 저번부터 GET 방식일 때 URL Encoding을 잘 해서 넘겨줘야겠다고 여러번 마음먹었음에도 불구하고, 또 이렇게 문자 자체들을 넘겨주다가 예약문자를 만나 문제를 헤매게 되었다. 하지만 덕택에 #이라는 예약문자가 있다는 것을 알게 되긴 했다.
#은 &같은 메타문자처럼 예약문자로, 12345까지 데이터로인식시키고 #부터 끊어서 뒤를 가져가버리는 식이였다.
그렇기 때문에 뒤의 URL들이 전부 읽히지 못해서 pw에는 값이 들어가지도 못했던 것이고, 예약문자인 # 이전까지만 id의 데이터로 인식되어 전달된 것이다.
즉 이 문제의 정답은 #을 Url Encoding해서 넘겨주면 된다. 난 이번 경험으로 Url Encoding의 중요성을 뼛속깊게 되새기게 되었다.. 정말 혼동과 혼란의 시간이였다. 역시 나는 말로해서 안되는건가, 맨날 Url Encoding해서 넘겨주겠다고 마음먹었음에도 이렇게 당해봐야 진짜 해야겠다고 느낀다..
?id=admin\&pw=or 1=1%23
->
query : select id from prob_succubus where id='admin\' and pw='or 1=1#'
정리 :
1. 주석이 문자열로 감싸져 있으면, 문자 일 뿐이지 주석 기능을 하지는 못한다. (명령어 같은 다른것들도 마찬가지..'문자'일 뿐이다.)
<?php
$query = "select id from prob_succubus where id='1234#' and pw='12345'";
echo $query;
>
Output:
select id from prob_succubus where id='1234#' and pw='12345'
public class hi_exam {
public static void main(String args[]) {
String str = "hello//hell";
System.out.println(str);
}
}
Output:
hello//hell
이 외 다른 언어들도 마찬가지. (일부언어만 예시로 들었다)
주석인 // 이런걸 넣으면 $input = '//' 이 주석 역할의 //이 아니라 문자 자체의 //이 된다는 것. 마찬가지로 //의 인코딩 값인 %2F%2F을 넣었을 때에도 $input = '//' 이렇게 똑같이 들어간다.
정리한 내용엔 틀린거 없는데, 애초에 질문부분이 잘못되어서 다 선 그어놓음.
2. 1에 이어서 헷갈리지 말아야 할 것은, "데이터가 url인코딩으로 넘겨져서 url 디코딩 되어 읽어들여지는 서버 구조에서, 만약 입력하는 데이터가 문자열 안으로 삽입된다면 url디코딩 되지 않고 url인코딩 상태 그대로 삽입되는가?" 에 대한 대답은 X이다.
$_GET['']이 문자열 안에 들어가면 UrlDecoding 안된 상태로 사용되고, 문자열 안이 아니면 UrlDecoding 상태로 사용된다? 이런 이야기는 말이 안되는 개연성이다.
서버에서 url decoding을 거친 뒤에 데이터를 사용하는 GET 방식의 구조에서,
이미 $_GET[''] 을 통해 사용할 때엔, 이 안에 디코딩 된 값이 들어있다 이 말이다
이 변수가 문자열 안에 들어가던 밖에 들어가던 상관없이 말이다. ( 물론, 지금부터 이야기할 서버는 존재하지 않겠지만... 서버가 url decoding을 거치지 않고 그냥 데이터를 사용한다면..그런 경우엔 디코딩 안된 값이 들어있겠다. 하지만 중요한 점은, 그 경우나 이 경우나 일단 디코딩/인코딩 상태 자체가 항상 통일적이라는 것이다. 디코딩이 되었으면 된거고 안되었으면 안된거지, 문자열 안에서 쓰이면 안되고 문자열 밖에서 쓰이면 된다? 그런 독립변수와 종속변수는 말이안된다는 것. 물론 ㅋㅋㅋ 문자열 밖에서 쓰이는 코드와 안에서 쓰이는 코드 사이에 갑자기 인코딩/디코딩을 해버리면 둘의 결과는 달라지겠지만, 이건 우리가 내민 원인결과에 해당되는 이야기가 아니다.)
쉽게말하면 두 사람이 통신하는데, 암호화 복호화 과정을 송수신 과정 안에서 거치게 된다고 보면 된다. (물론 암호화 복호화랑 인코딩 디코딩의 의미는 다르다. 암호화:알아들을 수 없게, 인코딩:정해진약속.) 그런 방식이라고 생각하면 이해하기 쉽다.
+ 인코딩 디코딩관련해선, 이전에 헷갈려서 똑같이 다룬 적 있는 내용인데, 안헷갈리다가 문자열과 주석 관련한 혼동 때문에 다시 혼란이왔었다...
3. URL에서는 &같은 예약문자 중에 #이 있다.
출처 -
첫번째 혼동 관련 참고자료
두번째 혼동 관련 참고자료
+ 글을 다 정리하고 읽다가, 2번의 경우에 대해 괜히 정리했다는 걸 알게 됐다.
$id = "id = '$_GET['id']'" 같은 경우만 문자열로 감싸져있다는 생각을 했는데, 결국
$no = "no = $_GET['no']"로 하던, $no = $_GET['no']로 하던 문자가 들어가면 따옴표로 감싸질 수 밖에 없다.
그냥 질문의 이유가 사라졌다.
질문은, "데이터가 전송될 때 Url Encoding을 거치고 서버에서 Url Decoding을 한 후에 데이터를 사용하는 구조에서, 입력한 데이터가 문자열 안에 삽입될 경우엔 url decoding이 안된 인코딩 상태 그대로 있고, 문자열 안에 삽입되지 않아야 url decoding이 된 상태가 되느냐"에 대한 질문이였는데, '문자열로 감싸지던 안감싸지던' 의 경우는 없고 그냥 항상 감싸진다. 질문은 두 경우에 대한 질문이였는데, 두 경우의 구분이 애초부터 없다. 질문이 의미가 없어졌다.
참고로 정리한 내용에 틀린 내용은 없다. 정리할 때에도 답은 '두 경우의 결과는 같다' 였는데 애초에 두 경우는 한 경우였다.
(비록 두 경우가 애초부터 한 경우라는걸 눈치채진 못한 채 정리했지만, '두 경우 결과는 똑같다' 라고 설명했다)
결국 2번에 대해 한마디로 정리하면,
데이터가 전송될 때 Url Encoding을 거쳐서 서버로 전송되는 상태에서, 서버에서 Url Decoding을 하고 데이터를 사용하는거면 그냥 다 디코딩 되어있는 거고($_GET[] 변수들), 안하고 사용하면(그런 서버는 없겠지만) 다 인코딩 상태인 거고... 도중에 인코딩 디코딩을 코드내에서 바꾸지 않는 한 ㅋㅋ, 이렇게 통일적이다.. - 음.. 결국 당연한 얘기로 돌아왔다.
간단한 코드로 설명을 돕자면,
$id = "id ='$_GET['id'] and no = $_GET['no']";
$no = "no = '$_GET['no']'";
$no_2 = $_GET['no'];
바로 위에서 말했듯, 어디는 인코딩 안되어서 들어가는데 어디는 인코딩 되어서 들어가고 이렇게 다르지 않다는 것.
'[웹해킹] > [LOS]' 카테고리의 다른 글
[LOS] NIGHTMARE (0) | 2020.12.25 |
---|---|
[LOS] ZOMBIE_ASSASSIN (1) | 2020.12.22 |
[LOS] ASSASSIN (0) | 2020.12.18 |
[LOS] GIANT (0) | 2020.12.17 |
[LOS] BUGBEAR (0) | 2020.12.16 |