前言
neoreg的php隧道在之前测试过程中,经常会出现无法使用的情况,所以索性分析下他的代码,并进行优化。
备注
调试
python neoreg.py -k debug_all -u http://192.168.111.1/neo/tunnel.nosocket.php --proxy http://127.0.0.1:8080 -vv
python neoreg.py -k debug_all -u http://30.1.20.12/tunnel.aspx --proxy http://127.0.0.1:8080 -vv
python脚本中断
PC上面ctrl+break
笔记本上面 ctrl+Fn+B
php session
Session 的工作机制是:为每个访客创建一个唯一的 id (UID),并基于这个 UID 来存储变量。UID 存储在 cookie 中,或者通过 URL 进行传导。
分析
php的脚本为啥没法使用,首先端口转发分为四个指令,CONNECT,DISCONNECT,READ,FORWARD
指令顺序一般是CONNECT-->FORWARD-->READ-->DISCONNECT
在jsp/aspx里,socket套接字可以保存在会话里,在FORWARD的时候调出来使用,但php没法保存在会话里,fsockopen是resource类型变量,当前请求全部执行完,当重新请求时,PHP就会释放资源,因此保存了再访问取出是空值,所以作者将会话放在CONNECT的逻辑里,想让当前请求一直保持着并通过flush提前返回session,但测试很多时候不会提前返回session,一直卡在循环里。CONNECT里使用了flush()可以将缓冲区数据立即发送回客户端,但在一些情况下并不生效,所以才导致有的人反馈能用,有的却无法使用。(这里测试在phpstudy环境下是用不了的,实战遇到几个环境也不行,xampp可以,linux安装的lamp也行)
php传统CGI是顺序执行,要想实现返回后继续执行,需要使用异步。
PHP-FPM提供的函数fastcgi_finish_request可以冲刷(flush)所有响应的数据给客户端并结束请求,但需要组件支持,这个不一定有。
测试数据包
1 2 3 4 5 6 7 8 9 | POST /neo/tunnel.nosocket.php HTTP/1.1 Host: 192.168.111.1 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Accept-Encoding: gzip, deflate Accept: */* Connection: close X-CMD: CONNECT X-TARGET: MTgwLjEwMS40OS4xMXw4MA== Content-Length: 0 |
1 2 3 4 5 6 7 8 9 10 11 12 | POST /neo/tunnel.nosocket.php HTTP/1.1 Host: 192.168.111.1 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Accept-Encoding: gzip, deflate Accept: */* Connection: close X-CMD: FORWARD Content-Type: application/octet-stream Cookie: PHPSESSID=7pb5eamlmfvb0bepdl71r24ea5 Content-Length: 56 R0VUIC8gSFRUUC8xLjENCkhvc3Q6IHd3dy5iYWlkdS5jb206ODANCg0K |
思路
多增加一个指令INIT,做转发端口探测获取到session就好了,connect再重新发起请求进行连接运行while循环做监听,不接收,没必要非得一次请求同时获取会话和建立连接,这样就可以解决了。
PHP代码就是拆分成两部分
这里设置sleep是做缓冲,防止循环太快,占用CPU
接着就是修改python脚本
指令名称新增INIT,这个后续需要混淆的,现在只是因为debug是明文。
第一次请求提交的header从CONNECT修改为INIT,这里做了一个后缀判断,因为只适用于php。
然后发起一个CONNECT请求,这里没有携带cookie是因为第一次请求后已经处理将cookie设置到self.conn里了。并且这里设置timeout,是因为CONNECT请求是个死循环不会返回,所以通过短超时快速断开防止阻塞。
测试
以上几处改完基本就ok了,最终效果如下,但是rdp连接就非常慢了,这个需要调整读写大小看看,毕竟他是挂着一个进程在等待接收发送,不像其他脚本再转发的时候直接读取socket就行了。
fgets第二个参数,设置为513字节读取,可能根据python脚本修改该值,rdp远程可以操作,不会卡住了。
关于读取缓冲区大小,这里其实有区分的,均通过参数--read-buff设置
生成服务端脚本时,读取缓冲区是为服务端设置的,默认是513 - 513%3=513,他这凑的3的整数倍,也不知道为啥。
连接服务端时,读取缓冲区是为客户端设置的,默认大小为2048,最大不能超过2600。
我将生成脚本的缓冲区默认值修改为1023了。
使用总结
生成混淆脚本,这个是根据script目录下的文件生成的。
1 | python neoreg.py generate -k 2333 --read-buff 1024 |
连接
1 | python neoreg.py -k 2333 -u http://30.1.20.12/tunnel.nosocket-new.php |
调试
1 2 | python neoreg.py generate -k debug_all python neoreg.py -k 2333 -u http://30.1.20.12:81/tunnel.nosocket-new.php --proxy http://127.0.0.1:8080 -vv |