반응형

1. 문제 설명

 

문제를 클릭해보자.

 

클릭하면 화면에 javascript challenge라는 문구 말고는 아무것도 없다.

 

쿠키값을 봤을때도 아무것도 없어 페이지 소스보기를 눌러보았다.

 

페이지 소스를 봤을때 딱히 다른건 없었지만 <script> 태그에 이상한 이모티콘이 존재했다.

 

따라서 뭔가 이번 문제는 <script> 태그에 난독화 되어 있는 코드를

 

복호화하는것이 우선일 것이라는 생각이 들었다.

 

따라서 구글에 다음과 같이 입력해보았다.

 

 

검색을 해보니 제일 위에 aaencode라는 것이 나온다.

 

클릭해보자.

 

 

클릭해 보니 alert("Hello, JavaScript")이 위의 이모티콘과 같다고 한다.

 

브라우저 콘솔에 넣어보자.

콘솔에 넣고 입력해보니 바로 alert가 떴다.

 

따라서 이번 문제를 풀기 위해 사이트에서 소개하는 Decoder-aaEncode에 접속해보자.

 

Githtml 파일로 decoder가 있다.

 

다운로드 받아 보니 Input the aaencode here난독화된 코드를 넣으면

 

Decoded aaencode String복호화된 코드가 나온다. 

 

복호화 된 코드는 다음과 같다.

 

var enco='';
var enco2=126;
var enco3=33;
var ck=document.URL.substr(document.URL.indexOf('='));
for(i=1;i<122;i++)
{
  enco=enco+String.fromCharCode(i,0);
}
function enco_(x)
{
  return enco.charCodeAt(x);
}
if(ck=="="+String.fromCharCode(enco_(240))+String.fromCharCode(enco_(220))+String.fromCharCode(enco_(232))+String.fromCharCode(enco_(192))+String.fromCharCode(enco_(226))+String.fromCharCode(enco_(200))+String.fromCharCode(enco_(204))+String.fromCharCode(enco_(222-2))+String.fromCharCode(enco_(198))+"~~~~~~"+String.fromCharCode(enco2)+String.fromCharCode(enco3))
{
  location.href="./"+ck.replace("=","")+".php";
}

 

이제 위의 코드를 보면 제일 마지막에

 

location.href로 문제에서 원하는 php 페이지로 가는것이 목적인것같다.

 

2. 문제 풀이

 

먼저 위의 복호화된 코드를 브라우저 콘솔 창에 넣고 어떤 값이 리턴되는지 보자.

 

먼저 문제에서 최종적으로 원하는 것은 원하는 php 사이트로 가는것이다.

 

마지막 if문을 살펴보면 ck라는 변수가

 

=+String.fromCharCode(enco_(240))+String.fromCharCode(enco_(220))...

 

완성된 문자열같아야한다.

 

여기서 + 는 문자나 문자열을 이어주는 역할을 한다.

 

따라서 String.fromCharCode(enco_(240))를 알기 위해서는 enco_라는 함수를 먼저 선언해주어야하며

 

enco_ 함수내에 enco.charCodeAt(x)라는 것을 실행하기 위해서는 enco를 먼저 선언해주어야한다.

 

선언 한 다음 String.fromCharCode(enco_(240))를 실행해보자.

 

y가 나온다.

 

남은것도 다해보면 youaregod~~~~~~~!이라는 문장이 나온다.

 

 

마지막 코드 if문안에 ck라는 변수에 선언해주고 replace로 해주는데

 

콘솔창에서 해보면 다음과 같이 나온다.

하지만 replace가 되려면 =가 ck라는 변수에 존재해야하고,

 

ck라는 변수는 다음과 같이 정의되어 있다.

 

var ck=document.URL.substr(document.URL.indexOf('='));

 

따라서 URL에 12번URL/?=youaregod~~~~~~~!가 들어가야한다.

 

접속해보자.

 

 

 

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

11. old-11  (0) 2021.11.09
10. old-10  (0) 2021.11.08
9. old-09  (0) 2021.11.08
8. old-08  (0) 2021.11.05
7. old-07  (0) 2021.11.04
반응형

1. 문제 설명

 

먼저 문제를 클릭해보자.

 

문제를 클릭하니 다음과 같은 Wrong이라는 문구와 view-source가 있다.

 

역시 아무런 정보가 없으니 view-source를 클릭해보자.

 

다른 코드들은 볼 필요 없이 위의 PHP 소스코드를 분석해보자.

 

먼저 $pat이라는 변수에 어떤 패턴을 저장하였다.

 

따라서 첫 번째 pat이라는 변수에 저장되어 있는 패턴을 분석해야 한다.

 

그 후 preg_match 함수를 이용해 앞에서 정의한 패턴을 기반으로 regex를 수행한다.

 

perg_match에 대한 설명은 여기에 있다. (클릭)

 

regex를 수행하지만 어떤 문자열이나 값이 있어야 패턴을 적용할 수 있다.

 

그 값이 바로 $_GET['val'] 값이다.

 

따라서 GET 형식으로 URL 뒤에 ?val=어떤 값을 넣어줘야 한다.

 

그렇게 패턴에 매칭이 되면 solve(11) 함수가 실행되면서 문제가 clear 하게 된다.

 

여기까지 정리해보면 다음과 같다.

 

GET 형식으로 val을 값을 서버로 넘겨주는데

 

이때 값은 perg_match에 매칭이 되어야 한다. (그래야 solve(11) 함수가 실행)

 

perg_match에 매칭 되기 위해서는 pat이라는 변수에 저장되어있는 패턴을 분석해야 한다.

 

따라서 패턴 분석 후 값을 val에 넣어주면 이번 문제는 clear 할 수 있을 것 같다.

 

2. 문제 풀이

 

/[1-3][a-f]{5}_.*$_SERVER[REMOTE_ADDR].*\tp\ta\ts\ts/ 패턴을 하나하나 분석해보자.

 

첫 번째로 나오는 것이 [1-3]이다.

 

1-3은 값인 것 같고 먼저 [ ]가 무엇을 의미하는지 살펴보자.

 

 

[ ]은 지정된 두 문자 사이에 문자 코드가 있는 문자를 찾는다는 의미이다.

 

이것이 뭐냐? 쉽게 말해 범위라고 생각하면 된다.

 

[g-s]지정된 두 문자(g,s) 사이에 있는 모든 문자를 주어진 값에서 찾는다는 의미이다.

 

따라서, abcdefghijklmnopqrstuvwxyz에서

 

g, h, i, j, k, l, m, n, o, p, q, r, s만 패턴에 맞게 적용되는 것이다.

 

다음은 [a-f]이다.

 

똑같이 지정된 두 문자(a,f) 사이에 있는 모든 문자를 주어진 값에서 찾는다는 의미이다.

 

그렇다면 [1-3][a-f] 이 두 개를 사용해서 매칭 되는 문자열을 찾아보자.

 

먼저 위에 처럼 주어진 문자열에 [1-3][a-f]을 만족하기 위해서는 첫 글자가 1~3 사이의 숫자여야 한다.

 

1번을 조사할 때 첫글자가 4니깐 패스, 2번을 조사할때 5라서 패스

 

3번을 조사할때 3이고 바로 뒤에 오는 문자가 [a-f] 사이에 포함되니 3a가 매칭이 된다.

 

 

이제 다음은 {5}

 

5는 값이니깐 { }가 먼지 먼저 알아야 한다.

 

Developer에서 정의한 { } 다음과 같다.

 

{5}는 앞 표현식이 5번 나타나는 부분에만 적용된다는 의미이다.

 

적용된 부분인 aabab는 {5}의 앞 표현식 [a-f]가 총 5번 a, a, b, a, b로 매칭이 되는 것이다. 

 

3은 [a-f]의 앞의 표현식 [1-3]에 의해 매칭 되는것이다.

 

그다음은 _ 이건 말그대로 _ 문자를 의미한다.

 

. 은 새로운 개행이 나오기 전까지의 모든 문자를 의미한다.

 

 

*은 앞의 표현식이 0개 이상 연속으로 반복되는 문자를 의미한다.

 

 

 

 

그 다음은 $_SERVER[REMOTE_ADDR] 변수 값인데

 

REMOTE_ADDR은 내가 접속하는 IP 주소를 의미한다.

 

IP 주소는 구글에 what is my ip라고 검색해도 되고 아래의 URL에 들어가서 확인이 가능하다.

 

https://whatismyipaddress.com/

 

그렇다면 여기까지 매칭이 되게 val을 작성해보자.

 

[1-3][a-f]{5}_.*뒤에 원래 $_SERVER [REMOTE_ADDR] 이것이 들어가야 하지만 들어갔다고 가정하고

 

3 abcde_127.0.0.1가 매칭이 되었다.

 

이제 남은 것은 \tp\ta\ts\ts이다.

 

\t은 우리가 알고 있는 탭이다.

 

____p____a____s____s //____은 탭의 공백을 의미함.

 

pass라는 단어가 나온다.

 

따라서 val3abcde_자신의ip\tp\ta\ts\ts를 넣어주면 된다.

 

하지만 GET 방식이다.

 

GET 방식에서 URL 뒤에 ?val=3abcde_자신의ip\tp\ta\ts\ts를 넣어주면

 

\이 문자가 URL encoding이 되어 %5C가 들어가면서 %5Cp%5Ca%5Cs%5Cs가 된다.

 

따라서 탭의 의미를 주려면 %09를 넣어 줘야 한다.

 

 

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

12. old-12  (0) 2021.11.09
10. old-10  (0) 2021.11.08
9. old-09  (0) 2021.11.08
8. old-08  (0) 2021.11.05
7. old-07  (0) 2021.11.04
반응형

 


1. 문제 설명


 

문제를 클릭해보자.

 

문제를 클릭하면 다음과 같이 O가 하나 튀어 나온다.

 

그 후 아무런 정보가 없다...

 

쿠키값에도 아무런 정보가 없다...

 

따라서 페이지 소스보기를 눌러 소스를 분석해보았다.

 

문제를 클릭했을때는 보이지 않던 hackme라는 문구와 Goal이라는 문구를 볼 수 있다.

 

뭐지? 하다가 문제 페이지로 다시 돌아가보니 옆으로 스크롤을 할 수 있었다.

 

스크롤을 하다보니 다음과 같은 Goal 점선을 볼 수 있다.

 

 

흠 뭔가 저 OGoal에 위치시켜야 할것 같았다.

 

그럼 다시 페이지 소스를 분석해보자.

 

핵심으로 봐야할 코드는 <a>에 있는 코드들이다.

 

코드는 다음과 같다.

 

<html>
<head>
<title>Challenge 10</title>
</head>

<body>

......

<a id=hackme style="position:relative;left:0;top:0"

onclick="this.style.left=parseInt(this.style.left,10)+1+'px';

if(this.style.left=='1600px')
  this.href='?go='+this.style.left" onmouseover=this.innerHTML='yOu' onmouseout=this.innerHTML='O'>O
  
</a>

......

</body>
</html>

 

먼저 a 태그의 idhackme이다.

 

따라서 문제에서도 힌트를 주었다. 

 

a 태그를 해킹해라라는 의미이다.

 

그다음 onclick 이벤트를 볼 수 있다.

 

말그대로 마우스로 클릭하면 발생하는 이벤트이다.

 

아까 O를 마우스로 클릭해보자.

 

 

O에 마우스를 올리니 yOu라고 바뀌면서 오른쪽으로 이동한다.

 

마우스를 올리니 yOu라고 바뀌고 마우스를 밖으로 빼니 다시 O로 돌아왔다.

 

이런 행동을 하는 코드는 다음과 같다.

 

onmouseover=this.innerHTML='yOu' onmouseout=this.innerHTML='O'>

 

그럼 남은 코드는 다음과 같다.

 

onclick="this.style.left=parseInt(this.style.left,10)+1+'px';

if(this.style.left=='1600px')
  this.href='?go='+this.style.left"

 

클릭하면 +1px 만큼 움직이는것이다.

 

그리고 this.style.left 값이 1600px이 되면 this.href='?go='+this.style.left로

 

뭔지 모르지만 ..?go로 이동한다.

 

따라서 이번 문제의 목표는 this.style.left 값을 1600px로 바꿔주면되는것이다.

 


2. 문제 풀이


개발자도구(F12)로 변경해도되지만 나는 웹 프록시인 피들러(클릭)를 사용해볼것이다.

 

 

먼저 F12를 눌러 Capturing을 시작해준다.

 

그 후 서버에서 응답오는 Response 패킷을 변조 하기위해 Alt+F11을 눌러준다.

 

그 후 문제 사이트를 F5로 새로고침을 하면 다음과 같이 Response 값을 변조 할 수 있다.

 

 

원래 1인 값을 문제에 맞게 1600으로 변조시킨다.

 

변조 시킨후 Alt+C를 눌러 다시 브라우져에게 전달한다.

 

브라우저에게 전달 후 O를 클릭해보면 아래의 그림처럼 Goal안에 들어간다.

 

 

Goal안에 들어간 O에 마우스를 올리면 yOu로 바뀌면서

 

문제에서 정한 링크로 가면서 문제를 Clear할 수 있다.

 

 

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

12. old-12  (0) 2021.11.09
11. old-11  (0) 2021.11.09
9. old-09  (0) 2021.11.08
8. old-08  (0) 2021.11.05
7. old-07  (0) 2021.11.04
반응형

 


이번 9번 같은 문제는 상당히 이해하기가 어려울 수 있습니다.

개인적으로 의식의 흐름대로 정리한 결과이며 정리한 결과가 정답이 아닐 수 있습니다.

또한 다소 내용이 복잡할 수 있습니다.


 


1. 문제 설명


 

먼저 문제를 클릭해보자.

 

 

이렇게 숫자가 1,2,3이 나와있고 Password를 입력하는 폼이 있다.

 

1번을 눌러보자.

 

 

1번을 눌러보니 Apple이라는 문구를 볼 수 있다

 

역시 Password를 입력하는 폼이 있다.

 

그럼 2번을 눌러보자.

 

 

이번에는 Banana이다.

 

3번을 눌러보자

 

 

3번을 눌러보니 Secret이라는 문구와 함께

 

컬럼 id와 no이 있다고 알려준다.

 

또한 no 3's id is password이라는 문구가 주어진다.

 

해석이 맞는지는 모르겠지만 3번의 id는 패스워드라는 의미이다.

 

즉, 3번의 ID를 찾으면 그것이 Password이고 이게 이번 8번 문제에서의 원하는 값인것 같다.

 

일단 공격 할 수 있는 포인트는 다음과 같이 2곳이라고 생각했다.

  1. 숫자를 의미하는 no
  2. Password를 입력하는 곳

숫자를 의미하는 noURL에서 볼 수 있다.

  1. webhacking.kr/challenge/web-09/?no=1
  2. webhacking.kr/challenge/web-09/?no=2
  3. webhacking.kr/challenge/web-09/?no=3

nopassword싱글 쿼터(')를 입력해보았다.

 

no' 를 넣으면 다음과 같은 URL이 나온다.

  • webhacking.kr/challenge/web-09/?no=%27

Password에 ' 에 넣으면 다음과 같은 URL이 나온다.

  • webhacking.kr/challenge/web-09/index.php?pw=%27

 

하지만 pw싱글 쿼터를 넣으면 다시 redirect가 되지만 no싱글 쿼터를 입력하면 

 

다음과 같이 Access Denied이라는 문구를 볼 수 있다.

 

 

이로써 공격할 포인트는 password가 아니라 no이라는것을 알 수있다.

 

왜냐? 싱글 쿼터를 입력하였을때 Access Denied이 뜨는것은

 

SQLi를 방지하기 위해 필터링이 되었음을 보여주는것이다.

 

그렇다면 이 문제는 어떻게 풀어야할까?

 

먼저 no필터링이 안되는것을 살펴봐야한다.

 

또한 필터링에 걸리지 않은 SQL문을 가지고 no 3의 ID를 알아내면 이번 문제를 해결 할 수 있을것 같다.

 

하지만 어떤것이 필터링이 된건지 이전의 문제처럼 소스 코드를 볼 수있는곳이 없다.

 

따라서 하나하나 넣어보면서 반응을 봐야한다.

  • 필터링: ', or, ||, and, &&, 공백(%20,%0a), >, <, =, from, select

몇 번 시도하니 위에 처럼 정리가 되었는데 일단 SQLi 문제를 풀때 공백과 from, select가 필터링 되면

 

SQLi를 작성하기 거의 힘들다. 

 

따라서 이문제를 풀기위해서는 경험상 블라인드 SQLi를 이용해야한다.

 

따라서 바이패스 되는 문자는 공백이 당연히 안되니

 

공백을 대신할 수 있는 괄호가 필수적으로 필요할것이다.

 

no에 괄호를 넣어보자.

 

Access Denied는 뜨지 않는다.

 

즉, 괄호를 활용 할 수 있다.


또한 selectfrom을 넣을 수 없다.

 

따라서 ascii 함수와 substr를 활용해서 3에 대한 ID 값 한글자씩 뽑아야한다.

 

따라서 ascii 함수도 필터링 되어있는지 보자.

 

webhacking.kr/challenge/web-09/?no=ascii

 

  • 필터링: ', or, ||, and, 공백(%20,%0a), >, <, =, from, select, ascii

ascii 함수가 필터링 되어 있다면 substr로 뽑아야한다.

 

substr를 넣어보자.

 

Access Denied가 뜨지 않는다.

 

따라서 substr은 사용할 수 있다는 의미이다.

 

substr로 함께 쓰이는 함수는 항상 if문이다. (substr 설명 클릭)

 

if문은 if(조건, , 거짓)이다.

 

if문안에 조건문을 실행하였을때 참이면

 

if문의 두번째 인자인 , 거짓이면 if문의 세번째 인자인 거짓을 반환한다.

 

예를들어, if(1+1=2, 1,0)이면 조건문에 1+1=2이므로 참이되어 1이 반환되는 의미이다. 

 

그렇다면 if문을 활용해서 어떻게 블라인드 SQLi를 수행할까?

 

위에서 설명하였듯이 if문과 substr를 사용할 수 있다고 하였다.

 

if(substr(id,1,1)=id의 첫번째 hex값, 1,2)

 

이렇게 작성해보았다.

 

위에 페이로드를 조금 풀어서 설명하면 substr 함수로 id의 첫번째 글자 1개를 가져온다는 의미이다.

 

그게 id 첫번째의 hex값이 만약 a(0x61)이면 1이 반환이 되고 a가 아니면 2를 의미한다.

 

즉 1이 반환되면 Apple이라는 문구를 볼 수 있을것이고 2가 반환되면 Banana를 볼 수 있다.

 

넣어보자.

 

webhacking.kr/challenge/web-09/?no=if(substr(id,1,1)=0x61,1,2)

 

Access Denied가 뜬다.

 

그렇다면 여기서 =이 필터링 되어있다는것도 알 수 있다.

 

따라서 =를 대신할 수있는 likein을 넣어보자.

 

like를 먼저 넣어보자.

 

webhacking.kr/challenge/web-09/?no=if(substr(id,1,1)like(0x61),1,2

 

 

????

 

Apple이 보인다.

 

그럼 id의 첫번째 값이 a라는 소리인데 혹시 잘못 생각할 수도있으니 

 

webhacking.kr/challenge/web-09/?no=if(substr(id,1,1)like(0x62),1,2)

 

a가 아닌 b를 넣어보자.

 

Apple이라는 문구가 없다.

 

따라서 블라인드 SQLi가 먹혔다.

 

이걸 가지고 이제 문제를 풀어보자!


2. 문제 풀이


위의 문제 설명에서 substr, if, like 또는 in을 사용 할 수 있는것을 확인하였다.

 

substr로 문자를 뽑아 내기 위해서는 3번의 id의 전체 길이를 알아야한다.

 

그래서 코드를 작성해보았다.

webhacking.kr/challenge/web-09/?no=if(length(id)like(5),1,2)일 때는

 

위의 실행 결과 처럼 ID의 길이가 5자리라고 나온다.

 

ID의 자리가 5자리면 'Apple'이다.

 

우리가 원하는 ID값은 no의 값이 3번일때이다.

 

그렇다면 webhacking.kr/challenge/web-09/?no=if(length(id)like(6),2,1) 일때는

 

길이가 6일때는 Banana이니깐 참이다.

 

따라서 if문에서는 참인 결과인 2가 반환되고 no에는 2가 들어가기때문에 Banana 문구가 나와야한다.

 

Apple이 나온다.

 

이게 뭘까?

 

왜 Apple이 나올까???

 

???????????????????????

 

위의 쿼리를 다시 보면 id의 길이가 6이면(banana)이면 참이니깐 2를 반환해서 Banana가 나와야한다.

 

테이블의 정보가 아래 처럼 있다고 가정하자.

 

 

그리고 밑에 쿼리문이 있다고 가정하자.

위의 쿼리문을 다시 해석하자면 id의 길이가 5이면 참인 1이 리턴될것이고

 

id의 길이가 5가 아니면 2리턴될것이다.

 

하지만 여기서 length(id),id의 길이 물었으나 어떤 no의 대한 id인지

 

정보가 없으니 no이 1에서 3까지 전부다 위의 쿼리를 실행하면서

 

조사할것이다라고 생각을 하였다.

 

그 후 다음과 같이 총 3개의 쿼리를 날려 보았다.

 

 

첫번째만 Apple이라는 문구가 나오고 나머지 2개의 쿼리에서는 어떠한 문구를 볼 수 없었다.

 

많은 생각에 걸쳐 다음과 같이 결론을 내렸다.

 

  1. 총 3개의 id에 대해서 조사를 진행한다.
  2. 그 중 첫번째 no이 1일때를 조사한다.
  3. no이 1일때의 id는 apple이다.
  4. 길이가 5자리이다.
  5. 따라서 if문의 최종적인 리턴값은 1이다.
  6. if문의 최종적인 리턴값인 1과 현재 no이 1일때 조사하고있으니 1=1이다.
  7. 1=1이니 최종적으로 true가 Apple이 반환된다.

다시 한번 예를들어 보자.

 

id의 길이가 6일때를 보자.

 

결과부터 보자면 다음과 같은 결과가 나온다.

 

 

아까 위에서 no이 1번에서 3번까지 있기 때문에 총 3번을 조사한다고 하였다.

 

no이 1일때를 살펴보자.

  1. no이 1일때를 보니 id값이 Apple로 총 id의 길이가 5이다.
  2. 따라서 id의 길이가 6인지를 판단했을때 이는 거짓이 되므로 if문의 거짓인 1이 리턴된다.
  3. if문의 최종적인 리턴값은 1, 또한 현재 테이블에서 조사하고 있는 no이 1이다.
  4. if문의 최종적인 리턴값 1과 현재 조사하고있는 no의 1이 같으니 1=1이다.
  5. 따라서 1=1이기 때문에 결과적으로 True니 Apple이 나온다.

no이 2일때를 살펴보자.

  1. no이 2일때를 보니 id값이 Banana로 총 id의 길이가 6이다.
  2. 따라서 id의 길이가 6인지를 판단했을때 이는 참이 되므로 if문의 참인 2가 리턴된다.
  3. if문의 최종적인 리턴값은 2, 따라서 no=2로 들어가기 때문에 no이 2인 Banana가 리턴된다.
  4. if문의 최종적인 리턴값 2과 Banana의 no이 2로 같다.

따라서 2=2이기 때문에 결과적으로 True니 Bananna이 나온다.

no이 3일때를 살펴보자.

  1. no이 3일때를 보니 id값이 ????로 총 id의 길이가 ????이다.
  2. 따라서 id의 길이가 6인지를 판단했을때 이는 거짓이 되므로 if문의 거짓인 1이 리턴된다.
  3. if문의 최종적인 리턴값은 1, 따라서 no=1로 들어가기 때문에 no이 1인 Apple이 리턴된다.
  4. if문의 최종적인 리턴값 1과 3번의 id값인 ?????이 같지 않다.
  5. 따라서 1=3이 아니기 때문에 결과적으로 false이니 무시된다.

 

이렇게 총 3번을 돌면서 판단하는데 제일 먼저 값이 나온 no이 1일때의 Apple이 출력되고

 

뒤의 Banana도 쿼리상 출력은 되었지만 첫번째 Apple만 출력되는것이다.

 

그렇다면 no이 3일때의 id의 길이를 구해보자.

 

앞에서 한 방식 그대로 적용하기 위해 아래처럼 쿼리를 작성하였다.

 

이렇게 no이 3일때의 길이가 1에서부터 증가해서 만약 20까지 범위를 주었다면

 

20까지 증가하면서 no이 3일때의 길이가 맞을때 Secret이라는 문구를 볼 수 있다면

 

그것이 no 3의 길이라고 생각하고 코드를 작성하였다.

 

코드를 작성하고 결과 값을 보니깐 다음과 같았다.

 

 

위의 결과를 보면 id의 길이가 5일때 빼고는 나머지가 다 Apple이 나왔다.

 

왜 이렇게 나온지 살펴보자.

 

길이가 1일때 - if(length(id)like(1),3,1)

  1. 길이가 1인것은 테이블에 존재하지 않는다.
  2. 존재하지 않기 때문에 if문에서는 거짓인 1이 리턴된다.
  3. if문의 최종적인 리턴값은 1
  4. if문의 최종적인 리턴값 1과 no이 1인 경우는 Apple이다.
  5. Apple의 no은 1이다.
  6. if문의 최종적인 리턴값과 5번의 no을 비교했을때 같다.
  7. 따라서 Apple이 나온다.

길이가 5일때 - if(length(id)like(5),3,1)

  1. 길이가 5인것을 no이 1~3까지 순차적으로 조사한다.
  2. no이 1일때의 id의 길이를 보니 Apple로 총 5자리이다.
  3. if문에서 length(5)를 물었기 때문에 참이므로 if문의 참인 3을 반환한다.
  4. if문의 최종적인 리턴값은 3이다.
  5. if문의 최종적인 리턴값은 3이지만 현재 검색하는 no은 1일때이다.
  6. 리턴값과 현재 조사한 no이 다르기 때문에 값이 나오지 않는다.

길이가 6일때 - if(length(id)like(6),3,1)

  1. 길이가 6인것을 no이 1~3까지 순차적으로 조사한다.
  2. no이 1일때의 id의 길이를 보니 Apple로 총 5자리이다.
  3. if문에서 length(6)를 물었기 때문에 거짓이므로 if문의 거짓인 1을 반환한다.
  4. if문의 최종적인 리턴값은 1이다.
  5. if문의 최종적인 리턴값은 1이고 현재 검색하는 no은 1이다.
  6. 리턴값과 현재 조사한 no이 같기 때문에 값이 Apple이 나온다.
  7. no이 1일때가 끝나고 no이 2로 넘어간다.
  8. no이 2일때 id의 길이를 보니 Banana로 총 6자리이다.
  9. if문에서 length(6)를 물었기 때문에 참이므로 if문의 참인 3을 반환한다.
  10. if문의 최종적인 리턴값은 3이다.
  11. if문의 최종적인 리턴값은 3이고 현재 검색하는 no은 2이다.
  12. 리턴값과 현재 조사한 no이 다르기 때문에 값이 나오지 않는다.

여기까지 보고 느낀점은

 

no이 3일때의 id의 길이와 값을 구하기 위해서는 if문의 참일때에는 3이 들어가야하고

 

거짓을때는 1,2가 아닌 다른 숫자를 써야한다는것이다.

 

왜냐하면 이 이유는 위에 과정을 보면 알 수 있다.

 

따라서 0과 또는 4이상의 숫자를 아무 숫자나 입력하면 된다.

 

4를 넣어서 다시 코드를 돌려보자.

 

 

id의 길이가 11일때 no이 3일때 볼 수있었던 Secret이 나온다.

 

이는 위의 과정처럼 혼자서 생각해보자!

 

끝으로 이제 문제를 해결 할 수 있을것 같다.

 

substr을 활용해서 no이 3일때 id의 길이가 11자인 값을 뽑아내보자.

 

나온 결과를 Password 입력에 입력해보자.

 

하지만 안된다.

 

조금만 다시 생각해보니 MYSQL은 소문자 대문자를 구별하지 않는다.

 

따라서 다시 소문자로 바꿔서 입력해보니 Clear 되었다.

 

 

참 설명이 지저분하고 어렵다.

 

하지만 이렇게 하나 하나 풀어서 설명하는 곳이 잘 없어서 혼자 이해하는데 힘들었다.

 

이렇게 이해한것도 틀린거일 수도 있다.

 

지금까지 9문제를 풀었는데 가장 이해하기 어려웠던 문제인것 같다.

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

11. old-11  (0) 2021.11.09
10. old-10  (0) 2021.11.08
8. old-08  (0) 2021.11.05
7. old-07  (0) 2021.11.04
6. old-06  (0) 2021.11.03
반응형

 


1. 문제 설명


 

 

이번 문제도 SQLi 문제이다.

 

문제를 클릭해보자.

 

문제를 클릭하면 다음과 같은 화면을 볼 수 있었다.

 

 

 

이게 뭐지?

 

done! (0/70)??

 

뭔가 시도 횟수를 의미하는것 같았다.

 

역시나 뭐 별 다른 기능이 없어 view-source를 클릭해서 소스코드를 보았다.

 

역시나 소스코드가 길었다.

 

이럴때는 하나하나 분석을 해야한다.

 

$agent=trim(getenv("HTTP_USER_AGENT"));

$ip=$_SERVER['REMOTE_ADDR'];

if(preg_match("/from/i",$agent))
{
  echo("<br>Access Denied!<br><br>");
  echo(htmlspecialchars($agent));
  exit();
}

 

먼저 getenv 함수를 사용해서 HTTP_USER_AGENT 값을 가져온다.

 

가져온 값에서 trim 함수를 이용해서 나온 값을 agent라는 변수에 담는다.

 

그럼 getenv 함수와 trim 함수를 알아보자.

 

 

getenv 함수는 위와과 같이 정의 되어있다.

 

getenv 함수는 환경 변수의 값을 가져오는 함수이다.

 

그리고 인자 값에 가져오고 싶은 환경 변수 값을 적어주는데

 

문제에서는 "HTTP_USER_AGENT"가 인자로 들어가있다.

 

HTTP_USER_AGENT웹 사이트를 접속한 컴퓨터의 웹 브라우저 정보를 의미한다.

 

다음은 trim 함수이다.

 

 

trim 함수는 whitespace공백strip 하는 함수이다.

 

하지만 문장 가운데 존재하는 공백이 아니라 처음 시작과 끝의 공백을 제거하는 함수이다.

 

예를 들어,  __hello__라는 문장이 있다고 가정하자.

 

 

하지만 trim 함수를 사용하면 처음 시작 2개의 공백과 끝의 2개의 공백을 제거하여

 

다음과 같은 문장이 나온다.

 

 

여기까지 정리하자면 접속한 웹 브라우저의 정보를 읽어와 앞 뒤 공백을 제거한다는 의미이다.

 

다음 코드는 $ip=$_SERVER['REMOTE_ADDR'] 인데

 

REMOTE_ADDR는 웹 사이트에 접속한 컴퓨터의 ip 주소를 가져온다.

 

그 다음 preg_match 함수가 또 보인다. (preg_match -> 클릭)

 

regexp 하는 역할을 하는데 agent라는 변수에 들어간 웹 브라우저 정보에서 from을 필터링한다.

 

다음 코드를 보자.

 

$db = dbconnect();

$count_ck = mysqli_fetch_array(mysqli_query($db,"select count(id) from chall8"));

if($count_ck[0] >= 70)
{ 
  mysqli_query($db,"delete from chall8"); 
}

$result = mysqli_query($db,"select id from chall8 where agent='".addslashes($_SERVER['HTTP_USER_AGENT'])."'");

$ck = mysqli_fetch_array($result);

 

웹 브라우저 정보에 "from"이라는 단어가 없으면 dbconnect 함수를 이용해서 db에 접속한다.

 

그 후 다음과 같은 쿼리로 DB에 쿼리를 날린다.

 

말 그대로 chall8이라는 테이블에 id의 갯수가 몇개있는지 묻는 쿼리이다.

 

그 후 countid의 갯수를 count_ck라는 변수에 넣고

 

count_ck70개 이상이면 chall8 테이블을 삭제한다.

 

만약에 70개 미만이면 코드가 밑으로 내려와 DB에 다음과 같은 쿼리로 묻는다.

 

 

위의 쿼리의 의미를 해석 하기 전에 addslashes 함수에 대해서 알아보자.

 

addslashes 함수에 대한 설명은 여기에 자세히 설명이 되어있다. (클릭)

 

$_SERVER['HTTP_USER_AGENT']유저의 브라우저 접속환경을 의미한다.

 

브라우저 접속환경이 왜필요할까?

 

서버의 입장에서는 브라우저별로 스타일을 다르게 보여지게 하거나 필요한 동작이 다를 수 있다.

 

무튼 다시 쿼리문을 정리하자면 다음과 같이 정리할 수 있다.

 

 

그럼 해석 해보자면 chall8이라는 테이블에서 어떤 사용자의 user_agent 값이 있다면

 

user_agent 값을 가지는 id를 보여달라는것이다.

 

만약 나의 idtest이고 user_agent값이 123123이라면

 

chall8이라는 테이블에서 agent의 값이 123123id는 누구냐라는 의미이다.

 

쿼리상 문제가 없으면 id값(test)이 이제 ck라는 변수에 들어가게된다.

 

다음 코드를 보자.

 

if($ck)
{
  echo "hi <b>".htmlentities($ck[0])."</b><p>";
  
  if($ck[0]=="admin")
  {
    mysqli_query($db,"delete from chall8");
    solve(8);
  }
}

if(!$ck)
{
  $q=mysqli_query($db,"insert into chall8(agent,ip,id) values('{$agent}','{$ip}','guest')") or die("query error");
  echo("<br><br>done!  ({$count_ck[0]}/70)");
}

 

ck 변수에 이제 값(id)이 있다면 누구인지 echo 함수를 이용해서 알려주고

 

만약에 admin이라면 chall8이라는 테이블을 삭제 시킨 후

 

solve(8)이라는 함수를 실행 시키면서 문제를 clear 할 수 있다.

 

하지만 ck라는 변수 즉 user_agent로 조회를 했을때 id가 없다면 insert 함수를 이용해서

 

chall8이라는 테이블에 agent, ip, id 값을 각각 넣어준다.

 

여기까지 정리하면 다음과 같다.

 

해당 문제는 idadmin으로 맞춰줘야지만 solve(8) 함수를 실행시킬 수 있다.

 

그렇기 위해서는 adminuser_agent 값을 알아야한다.

 

그 이유는 id를 뽑기 위해서는 밑의 쿼리를 사용해야하기 때문이다.

 

 

하지만 우리는 adminuser_agent 값을 모른다.

 

아니면 chall8이라는 테이블에 admin이라는 id를 가진 값이 없을 수도 있다.

 

따라서 adminuser_agent 값을 알아내던, chall8이라는 테이블에 idadmin을 생성해줘야할것같다.

 


2. 문제 풀이


 

이번 문제를 풀기 위해선 2개의 가정을 생각해보았다.

  1. chall8이라는 테이블에 id가 admin이 있을 경우 -> admin의 user_agent 값을 알아내야함 or 우회
  2. chall8이라는 테이블에 id가 admin이 없을 경우 -> chall8에 id가 admin을 insert 해야함

위의 처럼 정리 해보았다.

 

확률상 1번일 확률은 적다고 생각했다.

 

왜냐하면 php 코를 봤을 때 adminuser_agent를 알아내기 위한 방법이 현재로써는 보이지 않았다.

 

따라서 2번의 가정으로 문제를 풀어봤다.

 

그 이유는 php 코드를 봤을때 아래의 그림처럼 insert문이 존재했기 때문이다.

 

 

그리고 insert에서 사용자의 입력이나 값이 들어가는곳은 2곳이다.

 

agentip이다.

 

ip는 서버에 접속한 클라이언트의 ip를 $ip=$_SERVER['REMOTE_ADDR']로 설정하기 때문에

 

ipSQLi를 하기는 어렵다고 생각하고 agent 변수에 

 

즉, user-agent 값을 수정해서 insert문을 이용해서 id가 'guest'가 아닌 'admin'으로 수정하려고한다.

 

user-agent 값을 수정하기 위해서는 web-proxy를 사용해야한다.

 

이렇게 웹 프록시로 보면 User-Agent에 접속한 브라우저나 환경 값들이 들어가 있다.

 

여기에 SQLi를 수행할것이다.

 

먼저 SQLi가 잘 먹히는지 확인하기 위해

 

프록시를 사용하여 User-Agentguest01이라고 입력하고 서버로 전송해보자.

 

 

 

chall8 테이블에 guest01이라는 user-agent값이 없으니 아래의 SQLiinsert할것이다.

 

그렇다면 다시 guest01를 그대로 user-agent 값에 넣어서 서버로 전송하면 어떻게 될까?

 

chall8guest01이 라는 user-agentinsert 되었기 때문에 "hi guest"라는 문구를 볼 수가 있다.

 

그렇다면 이제 user-agent를 변경해서 insert가 되는것을 확인했으니

 

chall8에 존재하지않는 user-agent 값과 뒤의 'guest' 부분을 'admin'으로 바꿔주고

 

다시 insert 했을때 사용했던 user-agent 값으로 변경하여 서버로 전송하면

 

"hi guest"가 아닌 admin이 될것이다.

 

페이로드는 다음과 같다.

 

이렇게 SQL문을 만들 수 있다.

 

뒤에 #이라고 넣어준 이유는 뒤에 남아 있는 원래의 SQL문을 주석 처리하기 위함이다.

 

 

이렇게 SQL문을 만들었으면 이제 직접 프록시를 이용하여 작성한 페이로드를 넣어보자.

 

 

guest02라는 새로운 user-agent 값과 아이피 주소, 그리고 guest가 아닌 admin을 넣어준다.

 

 

 

그 후 정상적으로 chall8이라는 테이블에 등록되었다는 done!이라는 문구를 볼 수 있다.

 

다시 guest02로 요청을 한다.

 

 

요청을 하게 되면 서버에서 clear 했다는 문구를 볼 수 있다.

 

 

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

10. old-10  (0) 2021.11.08
9. old-09  (0) 2021.11.08
7. old-07  (0) 2021.11.04
6. old-06  (0) 2021.11.03
5. old-05  (0) 2021.11.01
반응형

1. 문제 설명


 

 

문제도 풀기도 전에 이제 바로 보자마자 SQLi 문제이라는 것을 알 수 있다.

 

클릭해보자.

 

 

Admin page라는 문구와 "auth"라는 버튼과 view-source가 있다.

 

별 다른 기능이 없으니 auth라는 버튼을 클릭해보자.

 

 

"Access_Denied!"라는 문구와 함께 alert가 떴다.

 

다음 시도해볼 수 있는 것이 없기 때문에 view-source를 클릭해 소스코드를 살펴보자.

 

참 소스 코드가 길다...

 

하나 하나 보자.

 

$go=$_GET['val'];

if(!$go) 
{ 
  echo("<meta http-equiv=refresh content=0;url=index.php?val=1>"); 
}

echo("<html><head><title>admin page</title></head><body bgcolor='black'><font size=2 color=gray><b><h3>Admin page</h3></b><p>");

if(preg_match("/2|-|\+|from|_|=|\\s|\*|\//i",$go)) 
  exit("Access Denied!");

$db = dbconnect();

 

$go=$_GET['val'];를 보아 일단 GET 형식으로 서버로 val라는 파라미터 값을 보낸다는것을 알 수 있다.

 

그리고 if문의 조건문에 go라는 값, 즉 사용자로부터 입력 받는 val 값이 없으면 다시 refresh 된다.

 

무조건 어떤 값이던지 넣어야지 다음으로 넘어갈 수 있다는 소리이다.

 

그후 preg_match라는 함수를 볼 수 있다.

 

 

PHP 공식 홈페이지에 보면 친절하게 설명이 되어있다.

 

regular expression을 수행하는 함수이다.

 

문제에서는 인자값이 2개가 들어간다.

 

각각의 인자값은 다음과 같다.

 

 

"/2|-|\+|from|_|=|\\s|\*|\//i"는 패턴을 의미한다.

 

$go는 사용자가 입력한 val 값이 들어가있다.

 

정리하자면 사용자가 입력한 val=go에 패턴 "/2|-|\+|from|_|=|\\s|\*|\//i"

 

매칭이 되는것이 하나라도 있으면

 

exit("Access Denied!");가 실행된다.

 

패턴의 제일 앞에 2가 있으니 2로 테스트 해보자!

 

webhacking.kr/challenge/web-07/index.php?val=2라고 입력해보았다.

 

 

역시 "Access Denied!"라는 문구를 볼 수 있다.

 

그렇다면 어떤 글자가 매칭이 될까?

 

"/2|-|\+|from|_|=|\\s|\*|\//i"

 

일단 regex에서 패턴은 한 쌍의 슬래시(/) 문자 사이에 위치한다.

 

그 다음은 많이 보았던 | -> or이고 \s공백을 의미한다.

 

그 후 보이는 것은 i인데 나도 regex를 많이 사용해보지 않아서 i가 뭔지 궁금했다.


따라서 찾아보았다.

 

 

즉, 입력한 문자열을 패턴으로 검사하는데 검사할때 대/소문자를 구문없이 매칭한다는 말이다.

 

그렇다면 이제 큰 뼈대는 이해를 했고 이제 어떤것이 매칭이 되는지 살펴볼 차례이다.

 

"/2|-|\+|from|_|=|\\s|\*|\//i"

 

(문자를 필터링 하고 싶으면 \를 하나 추가해주어야한다.)

 

이렇게 총 9개의 문자들이 필터링 되어있다.

 

그 다음 DB랑 연결을 한다.

 

여기까지 정리하면 다음과 같다.

 

사용자가 입력한 valgo라는 변수에 넣고 go라는 변수에 담긴 사용자값에서 

 

preg_match 함수를 이용해서 패턴에 매칭이되면 "Access Denied!"라는 문구와 함께 exit가 된다.

 

그럼 일단 무조건 preg_match 함수에 걸리지 않게 val를 입력해야할것이다.

 

다음 코드를 보자.

 

$rand=rand(1,5);

if($rand==1)
{
  $result=mysqli_query($db,"select lv from chall7 where lv=($go)") or die("nice try!");
}

if($rand==2)
{
  $result=mysqli_query($db,"select lv from chall7 where lv=(($go))") or die("nice try!");
}

if($rand==3)
{
  $result=mysqli_query($db,"select lv from chall7 where lv=((($go)))") or die("nice try!");
}

if($rand==4)
{
  $result=mysqli_query($db,"select lv from chall7 where lv=(((($go))))") or die("nice try!");
}

if($rand==5)
{
  $result=mysqli_query($db,"select lv from chall7 where lv=((((($go)))))") or die("nice try!");
}

$data=mysqli_fetch_array($result);

 

그 다음 rand 함수를 통해 1~5까지 수를 rand라는 변수에 넣는다.

 

그 후 총 5개의 if문이 있는데 어느 곳에 들어갈지는 5분의 1의 확률을 가지고 있다.

 

일단 어느 if문에 들어가도 query가 수행하는 행위는 같다.

 

rand1일때를 기준으로 설명하면 다음과 같다.

 

mysqli_query 함수로 DB 핸들러와 쿼리를 인자로 넣어주면

 

DB에 입력한 쿼리로 요청하게 되고 오류 없이 실행되면 true, 에러가 발생하면 false를 반환한다.

 

따라서 true로 실행이되면 쿼리 결과값을 result 변수에 넣는다. 

 

만약 false가 난다면 "nice try!"라는 문구를 볼 수 있다.

 

예를들어 val에 패턴에 없는 문자 ^를 입력해보자.

 

 위의 퀴리는 당연히 error가 날것이며 그렇다면 "nice try!" 문구를 볼 수 있어야한다.

 

 

이처럼 nice try라는 문구를 볼 수 있다.

 

다시 정상적인 SQL문을 살펴보자.

 

만약 별 문제가 없다면 chall7이라는 테이블에 존재하는 lv 컬럼에서

 

사용자가 입력한 값이 존재하면 출력해주는 간단한 쿼리이다.

 

그 다음 코드를 보자.

 

if(!$data[0]) 
{ 
  echo("query error"); exit(); 
}

if($data[0]==1)
{
  echo("<input type=button style=border:0;bgcolor='gray' value='auth' onclick=\"alert('Access_Denied!')\"><p>");
}

elseif($data[0]==2)
{
  echo("<input type=button style=border:0;bgcolor='gray' value='auth' onclick=\"alert('Hello admin')\"><p>");
  solve(7);
}

 

만약에 쿼리한 결과값이 없으면 "query error"가 출력되고 exit로 종료가 된다.

 

이제 쿼리한 결과값이 1이냐 2이냐로 나뉘는데

 

1이면 제일 처음에 봤던 "Access_Denied!"이라는 alert창을 볼 수있다.

 

하지만 2이면 "Hello admin"이라는 alert창이 나오면서 solve(7)이라는 함수가 실행되면서

 

문제가 풀릴것이다.

 

따라서 우리가 원하는 최종적인 목표는 쿼리 결과가 2가 나와야 한다는것이다.

 

여기까지가 PHP 소스코드 설명이였다.

 

그럼 문제를 어떻게 풀어야할까?

 

최종적으로 정리하면 다음과 같다.

  1. val라는 값을 무조건 입력해야함.
  2. 총 9개의 preg_match에 걸리지 않게 입력해야함.
  3. rand 함수로 인해 총 5분의 1의 확률을 가짐.
  4. 입력한 val=go가 쿼리상 에러가 있으면 "nice try!"를 볼 수 있음
  5. 쿼리상 문제가 없으면 "query error", alert('Access_Denied!'), alert('Hello admin') 이 3개임
  6. 최종적으로 쿼리한 결과값이 2가 되야함

위의 총 6개의 리스트를 가지고 문제를 풀어보자.

 

 


2. 문제 풀이


 

먼저 최종으로 쿼리한 결과값이 2가 되야한다.

 

 

그러기 위해서는 $go라는 곳에 2를 넣어야한다.

 

하지만 preg_match에 의해 2는 필터링되어 있다.

 

 

근데 위에처럼 정리를 해보니 -,+,*,/가 존재한다.

 

이걸보고 아 사칙연산을 통해 2를 구하는걸 막기위해서 넣은거구나라고 생각이들었다.

 

보안에서 mod는 많이 활용된다.

 

그렇다면 5%3, 11%3mod를 통해서 2가 나오게 하면될것같았다.

 

따라서 11%3val에 넣어 서버로 전송해보았다.

 

 

"query eeror"라는 문구가 보인다.

 

그렇다면 총 5개의 if문에서는 아무런 쿼리 에러가 일어나지 않았으므로 이는 통과한것이다.

 

하지만 왜 정답에서 요구하는 2를 계산해서 $go에 넣었는데 왜 solve(7)이 실행이 안됬을까?

 

생각을 했다.

 

한참을 생각하다보니 조회하는 chall7 테이블에 lv 이름을 가진 컬럼 중 2가 없다는 소리이다.

 

따라서 아래처럼 11%3...을 통해서 저 쿼리문이 실행되고나면 결과값이 2가 나오게 해야한다.

 

또한 chr 함수를 이용해서 2를 표현 할 수 있다.

 

 

하지만 chall7에는 lv2인값이 없다.

 

그렇다고 insert를 할 수도 없고 따라서 union을 사용할 수 밖에 없다.

 


union이란 


 

영어 그대로 합동, 합친다는 의미로 2개의 select문을 합치는것을 말한다.

 

어떤 두 단체가 합병이나 합쳐질때는 서로의 이해관계나 기타 제약 조건이 반드시 따라온다.

 

크게 아래의 3가지로 볼 수 있다.

  1. UNION 내의 각 SELECT 문은 같은 수의 열을 가져야 한다. 
  2. 열은 유사한 데이터 형식을 가져야 한다.
  3. 각 SELECT 문의 열은 또한 동일한 순서로 있어야 한다.

그렇다면 union을 이용해서 2를 출력하게 해보자.

 

위에서 %s로 인해 공백을 사용할 수 없으니 공백의 의미를 가지는 괄호를 이용할것이다.

 

 

일단 처음 select문을 거짓으로 만들어줘야한다.

 

왜냐하면 아무리 첫번때 select문을 2로 만들어도 값이 chall7에는 2가 존재하지 않는다.

 

따라서 첫번째 select문을 거짓으로 만들고 union으로 2번째 select문으로 2를 만들어줘야한다.

 

내가 만들어낸 페이로드는 다음과 같다.

 

 

이것을 조금 더 풀어서 설명하면 다음과 같다.

 

 

앞의 select문은 false로 무시가 되고 뒤의 select문은 쿼리상 문제는 없으니

 

2가 테이블에 없어도 출력이 된다. (이 이유 때문에 union을 사용)

 

출력이 된다는것은 result2가 들어간다는것이고 최종적으로 data[0]에도 2가 들어가

 

solve(7) 함수가 실행이되어 문제가 clear 될것이다.

 

하지만 위에 작성한 페이로는 rand 함수로 1이 된 경우이며

 

해당 if문에 가서 보면 괄호가 한쌍이다. ()

 

따라서 3 뒤에 괄호 1개를 입력한것도 그 이유이다.

 

끝으로 위의 페이로드를 val에 입력해도 정답이 될 확률 또한 5분의 1일것이다.

 

총 3번만의 시도 끝에 clear 했다.

 

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

9. old-09  (0) 2021.11.08
8. old-08  (0) 2021.11.05
6. old-06  (0) 2021.11.03
5. old-05  (0) 2021.11.01
4. old-04  (0) 2021.11.01
반응형

 

이번 문제는 아마 코딩으로 푸는 문제인 것 같다.

 

이제 저 모양이 어떤 문제를 의미하는지 알 것 같다.

 

문제를 클릭해보자.

 

 

클릭하면 view-sourceID: guest, PW: 123qwe가 보인다.

 

입력하는 폼과 URL에 파라미터도 보이지 않기 때문에 view-source를 클릭하여 힌트를 찾아보자.

 

클릭하면 엄청나게 긴 PHP 코드가 나온다.

 

코드는 다음과 같다.

 

<?php
include "../../config.php";
if($_GET['view_source']) view_source();
if(!$_COOKIE['user']){
  $val_id="guest";
  $val_pw="123qwe";
  for($i=0;$i<20;$i++){
    $val_id=base64_encode($val_id);
    $val_pw=base64_encode($val_pw);
  }
  $val_id=str_replace("1","!",$val_id);
  $val_id=str_replace("2","@",$val_id);
  $val_id=str_replace("3","$",$val_id);
  $val_id=str_replace("4","^",$val_id);
  $val_id=str_replace("5","&",$val_id);
  $val_id=str_replace("6","*",$val_id);
  $val_id=str_replace("7","(",$val_id);
  $val_id=str_replace("8",")",$val_id);

  $val_pw=str_replace("1","!",$val_pw);
  $val_pw=str_replace("2","@",$val_pw);
  $val_pw=str_replace("3","$",$val_pw);
  $val_pw=str_replace("4","^",$val_pw);
  $val_pw=str_replace("5","&",$val_pw);
  $val_pw=str_replace("6","*",$val_pw);
  $val_pw=str_replace("7","(",$val_pw);
  $val_pw=str_replace("8",")",$val_pw);

  Setcookie("user",$val_id,time()+86400,"/challenge/web-06/");
  Setcookie("password",$val_pw,time()+86400,"/challenge/web-06/");
  echo("<meta http-equiv=refresh content=0>");
  exit;
}
?>
<html>
<head>
<title>Challenge 6</title>
<style type="text/css">
body { background:black; color:white; font-size:10pt; }
</style>
</head>
<body>
<?php
$decode_id=$_COOKIE['user'];
$decode_pw=$_COOKIE['password'];

$decode_id=str_replace("!","1",$decode_id);
$decode_id=str_replace("@","2",$decode_id);
$decode_id=str_replace("$","3",$decode_id);
$decode_id=str_replace("^","4",$decode_id);
$decode_id=str_replace("&","5",$decode_id);
$decode_id=str_replace("*","6",$decode_id);
$decode_id=str_replace("(","7",$decode_id);
$decode_id=str_replace(")","8",$decode_id);

$decode_pw=str_replace("!","1",$decode_pw);
$decode_pw=str_replace("@","2",$decode_pw);
$decode_pw=str_replace("$","3",$decode_pw);
$decode_pw=str_replace("^","4",$decode_pw);
$decode_pw=str_replace("&","5",$decode_pw);
$decode_pw=str_replace("*","6",$decode_pw);
$decode_pw=str_replace("(","7",$decode_pw);
$decode_pw=str_replace(")","8",$decode_pw);

for($i=0;$i<20;$i++){
  $decode_id=base64_decode($decode_id);
  $decode_pw=base64_decode($decode_pw);
}

echo("<hr><a href=./?view_source=1 style=color:yellow;>view-source</a><br><br>");
echo("ID : $decode_id<br>PW : $decode_pw<hr>");

if($decode_id=="admin" && $decode_pw=="nimda"){
  solve(6);
}
?>
</body>
</html>

 

핵심 코드들을 분석해보자.

 

if(!$_COOKIE['user'])
{
  $val_id="guest";
  $val_pw="123qwe";
  
  for($i=0;$i<20;$i++)
  {
    $val_id=base64_encode($val_id);
    $val_pw=base64_encode($val_pw);
  }
  
  .....
  
}

 

먼저 $val_id 변수에 "guest"$val_pw"123qwe"로 초기화시켜준다.

 

이것은 문제를 클릭하였을 때 볼 수 있었던 것과 동일하다.

 

그 후 for문으로 20번 id와 pw를 즉, "guest"와 "123qwe"를 base64 encode를 수행한다.

 

for문이 끝나면 "guest", "123qwe" 이 두 값을 20번 base64 encode 값이 들어가 있다.

 

공부하는 김에 base64가 먼지 짧게 알아보고 가자.


Base64

 

먼저 인코딩(encoding)이란, 파일에 저장된 정보의 형태나 형식을 데이터 표준화, 보안, 처리 속도 등을 위해 다른 형태로 변환하는 과정을 말한다.

 

특히 이메일, 동영상, 이미지 영역에서 많이 사용되며, 반대로는 디코딩(decoding)이라고 한다.

 

그중 Base64도 인코딩의 한 종류이며 말 그대로 64진법이라는 뜻이다.

 

쉽게 말해 데이터를 64진법으로 표현하는 방식이다.

 

2진법은 0,1로 2가지, 10진법은 0~9까지 10가지로 64진법은 총 64개로 표현이 가능하다.

즉 , 6bit로 문자 한 개를 표현할 수 있다는 의미이다.

 

따라서 8bit의 데이터(바이너리)를 6bit의 크기로 표현한다는 의미이고

 

24bit(3byte)를 단위로 하여 3개의 문자에서 4개의 문자를 얻게 된다.

 

이렇게 되면 3개의 문자에서 4개의 문자로 데이터의 양이 많아진다.

 

그럼 데이터의 양이 증가하는 데 사용하는 이유가 뭘까?

 

바로 통신과정에서 바이너리 데이터의 손실을 막기 위해서 사용한다.

 

간단한 예로 바이너리 파일의 원본 파일이 1,000byte가 있지만 이 데이터를 string 형태로 저장하면

 

1,406byte로 저장된다.

 

반대로 string 형태가 아닌 binary로 저장하면 1,000byte가 그대로이다.

 

이는 char 데이터는 non-signed 1byte : 양의 정수 (0~255 표현)

 

​byte 데이터는 signed 1byte : 정수 (-128~127 표현)

 

즉 표현할 수 있는 데이터의 범위를 넘어서 데이터 손실이 일어나는 것이다.


따라서 이렇게 데이터의 손실을 막고 안전하고 정확하게 데이터를 전송하기 위해 Base64를 많이 사용한다.


다시 본론으로 돌아와 다음 코드를 보자.

 

위에서 20번 base64를 돌린 값을 replace 해주는 방식이다.

 

예를 들어 20번 돌린 base64 값에 1이 있으면 !로, 2가 있으면 @로 replace 해주는 코드이다.

 

val_pw로 마찬가지다.

 

....

  $val_id=str_replace("1","!",$val_id);
  $val_id=str_replace("2","@",$val_id);
  $val_id=str_replace("3","$",$val_id);
  $val_id=str_replace("4","^",$val_id);
  $val_id=str_replace("5","&",$val_id);
  $val_id=str_replace("6","*",$val_id);
  $val_id=str_replace("7","(",$val_id);
  $val_id=str_replace("8",")",$val_id);
  
 ....

 

그다음 아래의 코드를 볼 수 있는데 Setcookie 함수를 이용해서

 

"user"라는 쿠키명에 replace를 거친 값을 넣어주는 것이다.

 

마찬가지로 "password" 쿠키명에 replace를 거친 값을 넣어준다.

 

....

  Setcookie("user",$val_id,time()+86400,"/challenge/web-06/");
  Setcookie("password",$val_pw,time()+86400,"/challenge/web-06/");
  
 ....

 

그럼 그렇게 쿠키값이 있는지 확인해보자.

 

 

위의 쿠키 값을 URL-decoding을 하고 보면 replace가 된 @를 볼 수 있다.

 

 

이처처럼 20번의 base64 econdingreplace가 잘 수행된 것을 확인할 수 있다.

 

다음 코드를 보자.

 

....

$decode_id=$_COOKIE['user'];
$decode_pw=$_COOKIE['password'];

....

 

아까 그림에서 확인한 user라는 쿠키 값과 pwassword라는 쿠키 값을

 

각각 decode_iddecode_pw에 초기화한다.

 

....

$decode_id=str_replace("!","1",$decode_id);
$decode_id=str_replace("@","2",$decode_id);
$decode_id=str_replace("$","3",$decode_id);
$decode_id=str_replace("^","4",$decode_id);
$decode_id=str_replace("&","5",$decode_id);
$decode_id=str_replace("*","6",$decode_id);
$decode_id=str_replace("(","7",$decode_id);
$decode_id=str_replace(")","8",$decode_id);

$decode_pw=str_replace("!","1",$decode_pw);
$decode_pw=str_replace("@","2",$decode_pw);
$decode_pw=str_replace("$","3",$decode_pw);
$decode_pw=str_replace("^","4",$decode_pw);
$decode_pw=str_replace("&","5",$decode_pw);
$decode_pw=str_replace("*","6",$decode_pw);
$decode_pw=str_replace("(","7",$decode_pw);
$decode_pw=str_replace(")","8",$decode_pw);

....

 

처음 replace는 숫자를 문자로 replace 하였지만 지금은 다시 문자를 숫자로 replace 해준다.

 

....

for($i=0;$i<20;$i++)
{
  $decode_id=base64_decode($decode_id);
  $decode_pw=base64_decode($decode_pw);
}

....

 

그 후 for문으로 20번 decode준 값을 $decode_id$decode_pw에 초기화해준다.

 

이러면 원래의 평문이 나올 것이다.

 

....

echo("<hr><a href=./?view_source=1 style=color:yellow;>view-source</a><br><br>");
echo("ID : $decode_id<br>PW : $decode_pw<hr>");

if($decode_id=="admin" && $decode_pw=="nimda")
{
  solve(6);
}

....

 

그 후 echo로 decoding 된 평문의 IDPW를 출력해준다.

 

이때 출력하는 ID와 PW가 "admin", "nimda"이면 solve(6)이 실행되면서 이번 문제를 clear 할 수 있다.

 

여기까지 무리 없이 이해가 되었다면

 

user와 password 값에 admin, nimda를 20번 encoding 한 후

 

replace 한 값을 쿠키 값에 넣어주고 새로고침을 하면 문제를 해결할 수 있을 것 같다.

 

그런데 조금만 생각해보면 replace는 안 해도 된다.

 

어차피 replace로 원래의 "admin", "nimda" base64 값을 구하기 때문에 

 

그냥 "admin", "nimda" 값을 base64 encoding을 20번 값을 쿠키값에 넣으면 된다.

 

바로 코드를 작성해보자.

 

 

코드는 이처럼 짧다.

 

나온 값을 쿠키에 넣고 새로 고침을 하면 

 

 

ID와 PW에 "admin", "nimda"가 출력되면서 6번 문제를 해결할 수 있다.

 

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

8. old-08  (0) 2021.11.05
7. old-07  (0) 2021.11.04
5. old-05  (0) 2021.11.01
4. old-04  (0) 2021.11.01
3. old-03  (0) 2021.10.19
반응형

 

문제를 보면 다음과 같이 또 DB 모양이 있다.

 

이번 문제도 SQLi 문제일까? 하는 생각에 문제를 클릭해보자.

 

 

문제를 클릭하면 다음과 같이 LoginJoin 창이 보인다.

 

Login을 눌러보자.

 

 

Login을 눌르면 이렇게 IDPW를 입력하는 창이 나온다..

 

별다른 정보가 없기 때문에 페이지 소스를 보았다.

 

 

페이지 소스 보기를 봐도 별 다른 정보가 없어서 test/test를 입력해보았다.

 

 

 

test/test을 입력하고 login을 클릭한 결과 Wrong password라는 문구와 함께 다시 login 폼이 보인다.

 

IDPW를 알아내기 위해 IDPWSQLi를 수행보았지만 별다른 반응이 없었다.

 

따라서 이번 문제를 풀기 위해서는 계정을 알아야할것같았다.

 

따라서 제일 처음으로 돌아와 Join을 눌러보았다.

 

 

Join을 눌러 보니 Access_Denied이라는 문구와 함께 Join을 할 수 없게 되어있다.

 

페이지 소스에서도 아무런 정보도 찾을 수도 없고, ID, PWSQLi도 안먹히고..

 

그 다음 할 것이 없어 URL 주소를 보았다.

 

로그인 화면의 URL은 다음과 같다.

 

https://webhacking.kr/challenge/web-05/mem/login.php

 

우리는 할 수 있는 시도를 다 해보았고 문제에 힌트가 될 정보가 없기 때문에

 

https://webhacking.kr/challenge/web-05/mem를 입력하여

 

디렉토리 인덱스 취약점이 있는지 살펴보았다. 

 

여기서 말하는 디렉토리 인덱스 취약점을 짧게 정리하면 다음과 같다.


웹 애플리케이션을 사용하고 있는 서버의 미흡한 설정으로 인해 인덱싱 기능이 활성화 되어 있을 경우

공격자가 강제 호출을 통해 서버내의 모든 디렉터리 및 파일에 대해 접근이 가능하며

웹 애플리케이션 및 주요 정보가 노출될수 있는 취약점이다.


https://webhacking.kr/challenge/web-05/mem를 입력하니 디렉토리 인덱스 취약점이 발견되었다.

 

 

여기서 우리가 원하는 페이지인 join.php 페이지를 볼 수 있다.

 

join.php 페이지를 클릭해보자.

 

 

클릭하니 bye라는 문구와 함께 검은색 창만 보인다.

 

뭐지??하고 맨날 보았던 페이지 소스를 보았다.

 

 

script 태그에 이상한 script가 존재하였다.

 

원래의 보통의 script가 아니였기 때문에 이게 힌트일 것 같았다.

 

HTMl 소스코드를 가독성 있게 보면 다음과 같다.

 

<html>
<title>Challenge 5</title>
</head>

<body bgcolor=black>
<center>
<script>
l='a';
ll='b';
lll='c';
llll='d';
lllll='e';
llllll='f';
lllllll='g';
llllllll='h';
lllllllll='i';
llllllllll='j';
lllllllllll='k';
llllllllllll='l';
lllllllllllll='m';
llllllllllllll='n';
lllllllllllllll='o';
llllllllllllllll='p';
lllllllllllllllll='q';
llllllllllllllllll='r';
lllllllllllllllllll='s';
llllllllllllllllllll='t';
lllllllllllllllllllll='u';
llllllllllllllllllllll='v';
lllllllllllllllllllllll='w';
llllllllllllllllllllllll='x';
lllllllllllllllllllllllll='y';
llllllllllllllllllllllllll='z';
I='1';
II='2';
III='3';
IIII='4';
IIIII='5';
IIIIII='6';
IIIIIII='7';
IIIIIIII='8';
IIIIIIIII='9';
IIIIIIIIII='0';
li='.';
ii='<';
iii='>';

...... 생략 ........

</script>
</body>

</html>

 

뭔가 딱 보자마자 바로 느낌이 왔다.

 

치환이다!!

 

예를 들어보자.

 

l='a';

 

말 그대로 l은 a이다. 

 

생략한 코드를 치환하면 다음과 같다.

 

<html>
<title>Challenge 5</title></head><body bgcolor=black><center>
<script>


...... 생략 ........

if(eval(document.cookie).indexOf(oldzombie)==-1) 
{
    alert('bye');
    throw "stop";
}

if(eval(document.'U''R''L').indexOf(mode'='+1)==-1)
{
    alert('access_denied');
    throw "stop";
}

else
{
    document.write('<font size=2 color=white>Join</font><p>');
    document.write('.<p>.<p>.<p>.<p>.<p>');
    document.write('<form method=post action='join.php'>');
    document.write('<table border=1><tr><td><font color=gray>id</font></td><td><input type=text name='+id+' maxlength=20></td></tr>');
    document.write('<tr><td><font color=gray>pass</font></td><td><input type=text name='+pw+'></td></tr>');
    document.write('<tr align=center><td colspan=2><input type=submit></td></tr></form></table>');
}
</script>
</body>
</html>

 

이렇게 위의 코드처럼 치환하면 문제를 풀 수 있을 듯하다.

 

코드를 분석해보자.

 

 

위의 코드는 현재 cookie값 중에 oldzombie 라는 값이 있는지 체크하는것이다.

 

즉, 현재 cookie값 중에 oldzombie가 없다면 bye라는 문구와 함께 alert창을 볼 수 있다.

 

따라서 먼저 이 부분을 우회를 해야할것같다.

 

따라서 cookie 이름은 test, 값은 oldzombie를 설정하는 명령어로 cookie값을 설정해보았다.

 

 

그리고 다시 join.php 페이지에 접속해보았다.

 

 

아까와는 달리 access_denied라는 문구를 볼 수 있다!!

 

그럼 그 아래의 코드 또한 우회 해주면 된다!!

 

document.URL 값은 다음과 같이 'https://webhacking.kr/challenge/web-05/mem/join.php' 값이다.

 

 

근데 위의 소스코드에서는 oldzombie처럼 mode=1이 있는지 확인하는것이다.

 

따라서 https://webhacking.kr/challenge/web-05/mem/join.php/mode=1를 입력해보았다.

 

 

이처럼 Join 할 수 있는 폼이 생겼다.

 

여기서 JoinIDPW를 입력하고 제출 버튼을 눌러보자.

 

 

 

이제 Login.php에 접속해서 가입한 정보로 로그인을 해보자!

 

 

이렇게 로그인은 된다.

 

하지만 admin이 아니라는 문구가 뜬다.

 

따라서 이 문제의 출제 의도는 admin으로 로그인을 해야 한다는 것이다.

 

그럼 다시 Join.php에서 admin으로 가입해보자.

 

 

 

????

 

이미 admin이라는 계정이 존재한다.

 

하지만 이번 문제는 admin으로 로그인을 해야 한다.

 

앞에서 로그인 화면에선 SQLi가 통하지 않았기 때문에 이번 문제는 Join.php에서

 

admin으로 가입을 해야한다.

 

가입할때의 패킷을 보자.

 

다음과 같다.

 

id와 pw라는 파라미터로 입력한 admin123POST 형식으로 서버로 넘어간다.

 

예전에 학교 다닐때 DB에 저장될때 띄어쓰기나 공백이 들어가면 저장되는 형태에 따라서

 

'admin'과 'admin '이 같다는걸 배운적이 있다.

 

따라서 id값에 'admin' 대신 'admin '로 서버로 보내봤다.

 

그 후 로그인을 해보았다.

 

 

기억을 더듬어 문제는 풀었는데 정확하게 기억이 나지 않아서 찾아보았다.

 

https://techblog.woowahan.com/2559/ 여기서 정말 자세히 설명이되어있다.

 


쉽게 정리하자면 mysql DB에서는 'a'와 'a ' 또는 ' a'는 같다라고 인식을 한다.

 

즉, 공백이 있어도 같은 문자나 문자열이라고 인식을 한다는것이다.

 

하지만 이건 DB에 저장될때의 자료형이 CHAR일때만 가능하다.

 

비교하려는 두 문자열의 길이가 다른 경우

 

짧은 쪽에 공백을 이어붙여 길이를 똑같이 만든 다음 비교하기 때문에 발생하는 일이다.

 

이런 식이라면 'admin'와 'admin  '를 비교해도 똑같을 수밖에 없다.


반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

7. old-07  (0) 2021.11.04
6. old-06  (0) 2021.11.03
4. old-04  (0) 2021.11.01
3. old-03  (0) 2021.10.19
2. old-02  (0) 2021.10.18
반응형

 

4번 문제이다.

 

3번 문제는 DB 모양이여서 SQLi 공격을 이용하면 될것같았지만

 

4번 문제는 뭘 의미하는지 모르겠다.

 

원래 저 모양은 소스코드나 코드를 의미할때 많이 쓰이는 그림인데...

 

일단 문제를 클릭해서 보자.

 

 

클릭하면 초록색 글씨로 어떤 값이 나오고 Passwrod를 입력하는 칸이 나온다.

 

test라고 입력하고 발생하는 패킷을 한번 보자.

 

POST 형식으로 key라는 파라미터 우리가 입력한 test를 볼 수 있다.

 

그 후 페이지 소스를 보니 딱히 뭐 별다른게 없어보인다.

 

따라서 문제에서 주어진 [view-source]를 클릭해서 소스를 보자.

 

 

[view-source]를 클릭하면 해당 문제의 소스코드를 볼 수 있다.

 

php 코드를 보여주는데 핵심은 여기인것 같다.

 

일단 sleep(1)함수가 먼저 보인다.

 

주석으로는 anti brute force가 적혀있다.

 

나는 이 주석을 보고 힌트를 얻었다.

 

일단 굳이 sleep을 준 이유도 없을것이고 주석으로 친절히 anti brute force로 적어준것으로 보아

 

몇번으로 해결된 문제가 아닌거 같았다.

 

그 다음줄을 보자.

 

if문을 크게 2개로 나눠보면 (isset($_SESSION['chall4']) ($_POST['key'] == $_SESSION['chall4']) 로 나눌 수 있다.

 

이 2개가 참이면 solve(4)가 실행되면서 문제를 clear 할 수 있을것 같다.

 

php에서 isset은 $_SESSION['chall4']) 이라는 변수가 설정되어있는지 확인하는 함수이다.

 

다음 우리가 password에 입력한 key값이 $_SESSION['chall4']) 와  &&,

 

즉 같다면 solve(4) 함수를 실행 할 수 있다.

 

그 후 코드를 보자.

 

$hash rand(10000000,99999999)."salt_for_you";

 

위의 코드는 rand 함수를 사용하여 랜덤한 숫자를 만들어내는 함수이다.

 

범위는 10000000 ~ 99999999이다.

 

그리고 뒤에 salt로 "salt_for_you"에가 존재한다.

 

예를들어 rand 함수에서 12345678가 생성되었다면

 

$hash 라는 변수에는 12345678salt_for_you가 저장된다.

 

그 다음은 $_SESSION['chall4'] = $hash;으로 위의 12345678salt_for_you의 값이 해당 변수에 저장된다.

 

마지막 코드는 for문을 돌리는데 12345678salt_for_you 값은

 

sha1 함수를 돌리고 다시 hash 변수에 넣어주고 이걸 500번 반복한다.

 

여기까지 정리해보자면 다음과 같다.

  1. 주석에 anti brute force이 있는걸 보아 한번에 답을 찾을수는 없을것 같다.
  2. 우리가 입력하는 key값 즉 password와 hash값이 같아야한다.

그럼 내가 출제자가 원하는 방향으로 문제를 해결하기위해서는 

 

rand 함수의 범위를 10000000,99999999를 주고 위에 솔트 문장을 짜서 brute force

 

값을 서버로 날려봐도 소용이 없다.

 

sleep 함수도 있고 주석에도 친절하게 anti brute force라고 적혀있으니..

 

조금 생각해보니 문제를 클릭했을때 무슨초록색으로 무슨 값이 나왔었다.

 

총 크기도 20 바이트고.. sha1으로 나온 hsah 값인것같다.

 

 

그러면 저기 나오는 초록색 hsah 값은 총 sha1을 500번 돌린 값이라는걸 알 수있다.

 

하지만 sha1이 처음 실행되는 인자에는 rand 함수에서 12345678가 생성되었다면

 

제일 처음 $hash 라는 변수에는 12345678salt_for_you가 들어간다.

 

그 후 sha1을 500번 돌린 hash값을 변수 $hash에 최종적으로 넣는다.

 

그 값이 우리가 볼 수 있는 초록색 값이고 이것은 

 

즉, 처음 만들어진 12345678salt_for_you이 값을 찾아야하는 문제인것 같다. (Point 1)

 

그럼 이 문제를 풀기위해서는 다음과 같이 생각할 수 있다.

  1. 먼저 문제에서 나오는 해쉬값을 본다.
  2. 그 후 문제처럼 rand(10000000,99999999)를 통해 숫자를 정한다.
  3. 나온 숫자 뒤에 "salt_for_you"를 합쳐준다.
  4. 나온숫자+salt_for_you(ex 12345678salt_for_you)를 sha1으로 500번 돌린다.
  5. 500번 돌린 hash값과 문제에서나오는 hash 값을 비교한다. 
  6. 맞으면 정답은 12345678salt_for_you
  7. 아니면 다시 처음부터 수행한다.

하지만 이렇게 하다보면 webhacking.kr 서버와 세션이 끊어질 수 있다.

 

실컷 답을 찾아 12345678salt_for_you를 입력하려고 할때 로그인을 다시해야한다.

 

그러면 다시 찾아야한다.

 

따라서 이번 문제는 핵심 포인트는

 

rand이 아닌 처음값인 10000000부터 마지막 값인 99999999 까지 값을 미리 사전에 만들어

 

sha1으로 500번 돌린 값을 미리 구해 비교하면 훨씬 빠르다. (Point 2)

 

그리고 모든 10000000~99999999 까지 모든 값을 할 필요도 없다.

 

물론 10000000~99999999 까지 모든 값을 사전에 준비하면 한번에 문제를 풀 수는 있지만

 

어차피 이번 문제는 확률문제이다.

 

따라서 나는 4분의 1인 값인 10000000~25000000만 미리 계산하였다.

 

이렇게 총 만들어진 파일의 용량은 761MB이다.

 

해당 소스는 다음과 같다.

 

이렇게 hash.txt라는 파일을 만든 후  문제에서 주어진 hash값을 파일에서 찾으면 이번 문제는 clear다.

 

 

 

 

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

6. old-06  (0) 2021.11.03
5. old-05  (0) 2021.11.01
3. old-03  (0) 2021.10.19
2. old-02  (0) 2021.10.18
1. old-01  (0) 2021.10.18
반응형

3번 문제도 2번 문제와 같이 DB 그림이 있다.

 

따라서 이번 문제도 SQLi를 이용해서 문제를 푸는것 같다.

 

클릭해보자.

클릭해보니 이렇게 이상한 판이 나왔다.

 

처음에는 뭘까?하고 숫자가 입력이 되어있길래 나도 숫자를 입력해야하나? 싶어서

 

빈칸을 클릭해보았다.

 

근데 아래의 5*5만 클릭이 되었다.

 

 

숫자를 입력하는줄알았지만 이렇게 뭔가 규칙을 맞춰야하는것 같다.

 

하지만 아직도 지뢰찾기나 이런 규칙이 있는 퍼즐? 퀴즈? 같은걸 별로 안좋아한다.

 

따라서 위의 그림처럼 그냥 다 클릭하고 solved라는 버튼을 클릭해보았다.

 

solved를 클릭하면 GET 형식으로 다음과 같은 URL로 서버로 요청한다.

 

https://webhacking.kr/challenge/web-03/index.php?_1=1&_2=1&_3=1&_4=1&_5=1&_6=1&_7=1&_8=1&_9=1&_10=1&_11=1&_12=1&_13=1&_14=1&_15=1&_16=1&_17=1&_18=1&_19=1&_20=1&_21=1&_22=1&_23=1&_24=1&_25=1&_answer=1111111111111111111111111

 

그리고 브라우저에서는 다음과 같은 모습을 볼 수 있다.

 

 

여기까지 정리하면

 

총 5*5 빈칸 하나가 의미하는건 URL의 파라미터에 _1, _2 ... 이렇게 표현이 되는것 같다.

 

예를들어 첫번째칸은 파라미터 _1...

 

그리고 클릭을 해서 검은색으로 되면 파라미터 값은 1이 되고 클릭이 안되면 0이 되는것 같다.

 

그리고 5*5이기 때문에 _1,_2...._25를 전부 합친것이

 

answer=1111111111111111111111111로 표현되는것 같다.

 

페이지 소스를 봐도 go()라는 함수가 사용자가 클릭한 칸을 전부 합쳐서

 

문자열로 1111111111111111111111111을 만들어 주는것 같다.

 

그래서 _1과 _2..._25에 SQLi를 수행 보았다.

 

근데 별 다른것이 없었다...

 

마찬가지로 answer에도 SQLi를 수행 보았다.

 

여기서도 별 다른 반응이 없었다.

 

그럼 어떻게 할까?

 

저 퀴즈를 풀어야하나???

 

근데 풀기가 너무 귀찮아서.

 

brute force로 그냥 보내버릴까? 해서 코드를 작성을 해보았다.

 

근데 짜던 도중 생각해보니 표현 할 수 있는 숫자는 0, 1이고 자릿수는 25자리다.

 

그럼 brute force를 수행한다면...  엄청 낭비다..

 

따라서, 퀴즈를 풀어야할것같다...

 

뭐 여러 사이트를 참조하고 문제를 푸는 방법을 익힌 후 문제를 풀고 solved를 눌러보았다.

 

 

이렇게 Clear 했다는 문구와 함께 log를 남기기 위해 이름을 입력하라는 문구가 나오고

 

submit 버튼이 나왔다.

 

따라서 test!!라고 입력을 해보았다.

 

test!!라고 입력해본 결과 입력한 이름 test!!과 아까 퀴즈에서 풀었던 answeriplog처럼 남겨졌다.

 

log을 남기기 위해 submit을 클릭했을때 발생하는 패킷은 다음과 같다.

 

 

POST 형식으로 answer에는 아까 퀴즈에서 푼것이 들어가있으며

 

파라미터 id에는 앞에서 입력한 test!!가 URL 인코딩 되어 있다.

 

여기까지 정리해보자면

 

URL: https://webhacking.kr/challenge/web-03/index.php?_1=1&_2=1&_3=1&_4=1&_5=1&_6=1&_7=1&_8=1&_9=1&_10=1&_11=1&_12=1&_13=1&_14=1&_15=1&_16=1&_17=1&_18=1&_19=1&_20=1&_21=1&_22=1&_23=1&_24=1&_25=1&_answer=1111111111111111111111111

  1. 위의 URL 파라미터에서는 SQLi가 발생하는 곳은 없었다.
  2. 퀴즈를 풀고 나니 log를 남길 수 있는 곳을 찾았다.
  3. log를 남길때 발생하는 패킷에 answer과 id라는 파라미터 2개가 생겼다.
  4. 3번에서 찾은 파라미터 2개에 대해서 SQLi를 수행해보자.

 

이렇게 answer의 마지막에 SQLi를 테스트하기위해 '를 입력하고 서버로 요청해보았다.

 

 

 

이렇게 query error!라는 문구와 함께 오류가 난다.

 

여기다! (Point 1)

 

여기서 SQLi를 수행하면 될것 같다.

 

 

이렇게 answer에 1'+and+1=1#이라고 입력을 해주었다.

 

하지만 이렇게 빈 페이만 나온다.

 

쿼리가 오류이면 분명 query error!라는 문구가 보일 것인데 보이지가 않았다.

 

다음은 answer에 1'+and+1=0#을 입력해보았다.

 

1'+and+1=1#나 1'+and+1=0#이 해당 쿼리를 만들어낸 과정은 다음과 같다.

 

  1. 원래의 answer 끝에 '를 삽입하자 query error!라는 문구가 보임.
  2. 따라서 여기서 SQLi를 수행.
  3. '를 삽입했으니 원래의 정상적인 쿼리 마지막에는 '가 한개가 남음.
  4. 이 마지막 한개의 '를 처리하기 위해 주석인 #을 추가함.

 

결과는 또 빈페이지...

 

뭘까? 하고 생각을 해보았다.

 

log에서는 3개를 볼 수있었다.

 

입력한 이름(id)와 answer 마지막으로 ip 이렇게 3개를 볼 수 있었는데

 

내가 입력한 answer 1'+and+1=1#, 1'+and+1=0#이 잘 먹힌지를 보기위해서는

 

먼저 log에 answer1이라는것을 먼저 등록을 해주어야 참/거짓의 반응을 살펴 볼 수 있을것 같다.

 

이렇게 answer에 1을 넣고 id에는 test를 넣고 먼저 서버로 보내 log를 남겼다.

 

서버에서는 쿼리상에 문제가 없기 때문에 이렇게 log를 남겨준다.

 

이제 다시 answer1'+and+1=1#, 1'+and+1=0#이 잘 먹히는지 살펴보자.

 

 

위의 그림에서 answer 부분이 1' and 1=1# and 기준으로

 

왼쪽 오른쪽이 참이기 때문에 위에서 남긴 로그가 나와야한다.

 

이렇게 잘나왔다.

 

이제 1'+and+1=0#를 입력하고 서버로 요청해보자.

 

여기서 만약에 빈페이지가 나온다면 여기서 SQLi를 수행하는것이 확실해 진다!

 

빈페이지가 나오는것으로 보아 여기서 SQLi를 수행하는것이 확실해 졌다.

 

따라서 앞의 문제처럼 DB명의 길이DB명을 알아보자!!

 

코드를 작성하여 DB의 길이를 알아내보니 10자리였다.

 

이제 DB의 길이를 구하고 이제 DB 명을 알아내야한다.

 

근데 가만히 생각을 해보니 이번 문제가 DB명을 알아내고 table명, column명을 알아낼 필요가 있을까?

 

뭔가 위의 과정을 통해 answer라는곳에 SQLi를 하는건 맞는거같은데..

 

flag값처럼 출제자가 원하는 answer라는 값을 알아내야하는건가?

 

그러면 DB명table명, column명을 알아내서 answer라는 값을 알아내야하는건 맞다.

 

근데 answer라는 값을 항상 참으로 만들면???

 

그래서 answer에 아래와 같이 1'or+'1를 넣어봤다.

 

풀렸다...

 

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

6. old-06  (0) 2021.11.03
5. old-05  (0) 2021.11.01
4. old-04  (0) 2021.11.01
2. old-02  (0) 2021.10.18
1. old-01  (0) 2021.10.18

+ Recent posts