이번 문제는 아마 코딩으로 푸는 문제인 것 같다.
이제 저 모양이 어떤 문제를 의미하는지 알 것 같다.
문제를 클릭해보자.
클릭하면 view-source와 ID: 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 econding과 replace가 잘 수행된 것을 확인할 수 있다.
다음 코드를 보자.
....
$decode_id=$_COOKIE['user'];
$decode_pw=$_COOKIE['password'];
....
아까 그림에서 확인한 user라는 쿠키 값과 pwassword라는 쿠키 값을
각각 decode_id와 decode_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 된 평문의 ID와 PW를 출력해준다.
이때 출력하는 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번 문제를 해결할 수 있다.