https://dreamhack.io/wargame/challenges/873
문제 설명

들어가면 이런 화면이 떠있다.

문제 설명에 나와있는 것처럼 알맞은 nickname과 password를 입력해야하는 것 같다.
F12를 눌러서 코드를 확인해보았는데 별도로 nickname과 password에 관한 내용이 없어 문제 파일을 봐보았다.
문제파일에는 step2.php라는 파일이 있었고 확인해보았다.
<div class="container">
<div class="box">
<!-- PHP code -->
<?php
// POST request
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$input_name = $_POST["input1"] ? $_POST["input1"] : "";
$input_pw = $_POST["input2"] ? $_POST["input2"] : "";
// pw filtering
if (preg_match("/[a-zA-Z]/", $input_pw)) {
echo "alphabet in the pw :(";
}
else{
$name = preg_replace("/nyang/i", "", $input_name);
$pw = preg_replace("/\d*\@\d{2,3}(31)+[^0-8\"]\!/", "d4y0r50ng", $input_pw);
if ($name === "dnyang0310" && $pw === "d4y0r50ng+1+13") {
echo '<h4>Step 2 : Almost done...</h4><div class="door_box"><div class="door_black"></div><div class="door"><div class="door_cir"></div></div></div>';
$cmd = $_POST["cmd"] ? $_POST["cmd"] : "";
if ($cmd === "") {
echo '
<p><form method="post" action="/step2.php">
<input type="hidden" name="input1" value="'.$input_name.'">
<input type="hidden" name="input2" value="'.$input_pw.'">
<input type="text" placeholder="Command" name="cmd">
<input type="submit" value="제출"><br/><br/>
</form></p>
';
}
// cmd filtering
else if (preg_match("/flag/i", $cmd)) {
echo "<pre>Error!</pre>";
}
else{
echo "<pre>--Output--\n";
system($cmd);
echo "</pre>";
}
}
else{
echo "Wrong nickname or pw";
}
}
}
// GET request
else{
echo "Not GET request";
}
?>
</div>
</div>
여기에서 중요하게 볼 코드는 이것이다.
if ($name === "dnyang0310" && $pw === "d4y0r50ng+1+13") {
echo '<h4>Step 2 : Almost done...</h4><div class="door_box"><div class="door_black"></div><div class="door"><div class="door_cir"></div></div></div>';
$cmd = $_POST["cmd"] ? $_POST["cmd"] : "";
Name --> "dnyang0310", p --> "d4y0r50ng+1+13"이면 step2로 넘어갈 수 있다는 사실을 알 수 있다.
하지만 입력 값에 필터링이 되어있어 해당 값들을 그대로 입력하면 제대로 로그인을 할 수 없다.
첫번째 조건은 pw가 소문자나 대문자가 포함되어있으면 아래 문구가 뜬다.
if (preg_match("/[a-zA-Z]/", $input_pw)) {
echo "alphabet in the pw :(";
}
즉, 비밀번호가 d4y0r50ng+1+13이지만 알파벳이 포함되어있으면 안된다는 것이다.
두번째 조건은 Name에서 "nyang"을 빈 문자열로 필터링한다. 그리고 pw는 정규표현식을 통해 필터링 된다.
$name = preg_replace("/nyang/i", "", $input_name);
$pw = preg_replace("/\d*\@\d{2,3}(31)+[^0-8\"]\!/", "d4y0r50ng", $input_pw);
즉, Name의 값이 dnyang0310이지만 빈 문자열로 바꿔버린다는 것이다...
그렇다면 어떻게 풀어야할까?
Name같은 경우, 필터링한 값이 dnyang0310 되야하므로 dnynyangang0310이라고 적으면 된다. 중간의 nyang만 없어지므로 필터링을 우회할 수 있다.
pw의 정규표현식을 풀어보면
\d* : 0 ~ 9까지 중 한 자리 숫자를 의미. 뒤의 *은 0개 이상 나타나야 한다는 것을 의미
\d{2, 3}(31) : 0 ~ 9까지 중 한 자리 숫자를 2번 혹은 3번 반복
+ : 지금까지의 표현을 1회 이상 반복
[^0-8] : 0~8 이외의 문자가 있어야한다.
이렇게 정규표현식을 해석해서 pw을 써보면 44@555319!+1+13 라고 만들어 주었다.


입력하니 step2로 넘어갔다.
입력 창에 command라고 뜨길래 무슨 명령어를 입력해야하는게 아닌가 싶어 소스코드를 다시 보았다.
// cmd filtering
else if (preg_match("/flag/i", $cmd)) {
echo "<pre>Error!</pre>";
}
else{
echo "<pre>--Output--\n";
system($cmd);
echo "</pre>";
}
}
else{
echo "Wrong nickname or pw";
}
보면 flag에 대한 필터링이 적용되어있다.
리눅스 명령어 중 *를 이용해서 코드를 쓰면되지 않을까 싶었다.
cat ../dream/*.txt
맨 위 사진을 보면 flag파일의 주소가 나와있어 그것을 이용해주었다.

