渗透测试实战回忆录
本文首发于先知社区 https://xz.aliyun.com/news/91353 回忆我这几年的失误和技术误解,并且分享有趣的故事—-几次打崩系统的故事,并且从中总结失败的教训,回忆稍显繁琐。本人已经没有参加任何比赛性质的攻防演练,均直接服务于项目,如有雷同,纯属巧合,所有渗透行为均合法授权,请不要瞎溯源。 第一次崩溃 大概是刚毕业出来工作的时候,在那个时候我还是个新手菜鸟,摆弄着各种大佬写的工具,那个时候,我开始熟悉冰蝎和Cobaltstirke,我发现了一个震惊我的功能,冰蝎居然能”一键上线CS“,对于不懂jni、汇编、架构和系统的那个时候的我,我震惊的,本地搭建环境测试了还成功了,轻松吊打360\微软DF,一个webshell 怎么可能打入EXE在内部执行? 但是测试过程出了一个问题,在我的本机搭建的环境测试非常顺利,一键上线绕过了我们国内主流的杀毒,上线执行命令一切正常,而我朋友Arui的机器上测试,每次都会崩溃,我们开始争辩,但是你懂的,讨论不出什么结论,因为我们那个时候知识有限,我们当时不得不得出这个技术不稳定的错误结论,哈哈哈。 那么到底为什么? 我和周边的朋友都是web出身的,基本上搞二进制的凤毛麟角,同时懂web和二进制这两个就更少了,22年当初问了一圈都没人能回答这个问题,过了几年,我开始掌握二进制的知识,然后翻阅了更新日志,看到了下面一条。 https://github.com/rebeyond/Behinder/releases 内存马shellcode注入前增加了CPU架构判断 4.1版本之前内存马shellcode居然没有CPU架构判断,所以答案已经很明晓,强制给32位的java塞一个64位的dll,架构都不一样肯定崩溃,也就是我同事的环境,可能还在用32位的java+windows测试环境,这要是实战,我们只能得出对面关闭了网站 我们再看一下源代码,这个是之前的64位和32位的判定: https://github.com/MountCloud/BehinderClientSource/blob/d146e76ec90dbb5f1454dd77ec8e2368b4eadf6f/src/main/java/net/rebeyond/behinder/ui/controller/ReverseViewController.java#L181 选择随机保存dll到如下目录 String remoteUploadPath = "c:/windows/temp/" + Utils.getRandomString((new Random()).nextInt(10)) + ".log"; 判定架构进入分支 (String)this.basicInfoMap.get("arch")).toString().indexOf("64") >= 0 是64位直接打入JavaNative_x64.dll,否则打入JavaNative_x32.dll if os == 64: inject JavaNative_x64.dll and shellcode else: inject JavaNative_x32.dll and shellcode 这样写修复了x86-64的问题,但是很明显,全世界有大量不同的架构,比方说ARM架构和RISC-V,这样就会直接进入inject JavaNative_x32.dll and shellcode打崩JVM 深入解释原因 等等,刚刚的就是正确的答案吗,崩溃的含义是什么?刚刚我们并没有解释清楚哪里崩了,对吧?已经是3年前的事情了,如果有dumpcrash的文件,我瞄一眼就能定位,但现在我只能尝试推测还原,让我们把问题分解开来,其实就是三部分:windows的问题、java自己的问题、载入的dll的问题: 首先,先理解JNI的本质只是调用LoadLibrary,一个64位的java.exe PE调用LoadLibrary加载32位的DLL是不会导致崩溃的,你可以编译一个64位的PE验证这一观点,它只会返回一个错误code回来,这可不会带崩进程和后续代码的执行逻辑: // demo.cpp : This file contains the 'main' function. Program execution begins and ends there. // #include <iostream> #include <Windows.h> int main() { std::cout << "SysWOW64 is 32 bit PE!\n"; LoadLibraryA("C:\\Windows\\SysWOW64\\ntdll.dll"); std::cout << "the lasterror is " << GetLastError() << std::endl; std::cout << "It is ok the countine execute code\n"; } 执行结果如下: ...