HCTF GAME RE - JAVA系列

这次HCTF GAME一共有三道java的逆向,都是z神出的。题目还是很基础的,但我作为一个java零基础的菜鸡,这些题目是够我做了。

re?

下载:http://pan.baidu.com/s/1kUZjUB1 密码:u4uw

hint:逆向不止是汇编

这其实就是一道代码审计题。

解压jar,得到图片ctf.jpg。用jd-gui.exe反编译,得到源码:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package game;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Main
{
public static void main(String[] args)
throws Exception
{
new Main().m();
}

private void m() throws Exception {
System.out.print("Now give me flag: ");
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
String flag = strin.readLine();
InputStream is = getClass().getResourceAsStream("/game/ctf.jpg");
int len = is.available();
if (len < 20008) {
throw new Exception("res error");
}

long r = 10000L;
while (r != 0L) {
r -= is.skip(r);
}
byte[] key = new byte[16];
int l = 0;
while (l != 16) {
l += is.read(key, l, 16 - l);
}

r = 10000L;
while (r != 0L) {
r -= is.skip(r);
}
byte[] iv = new byte[16];
l = 0;
while (l != 16) {
l += is.read(iv, l, 16 - l);
}

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec skey = new SecretKeySpec(key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(1, skey, ivSpec);
byte[] en = cipher.doFinal(flag.getBytes());
byte[] tFlag = { 69, -101, 74, -127, -13, 110, 17, -103, 112, -111, -87, 87, 45, -110, 38, -11 };
if (Arrays.equals(en, tFlag))
System.out.println("hctf{" + flag + "}");
else
System.out.println("try again");
}
}

看一下代码,就是一个简单的AES解密,用python写出解密函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Cipher import AES
fp = open("ctf.jpg",'rb')
fp.read(10000)
key = fp.read(16)
fp.read(10000)
iv = fp.read(16)
fp.close()

mode = AES.MODE_CBC
aes_obj = AES.new(bytes(key),mode,bytes(iv))

flag_enc = [69, -101, 74, -127, -13, 110, 17, -103, 112, -111, -87, 87, 45, -110, 38, -11]
for i in range(len(flag_enc)):
if flag_enc[i]<0:
flag_enc[i]+=256
flag_enc[i] = chr(flag_enc[i])
flag_enc = ''.join(flag_enc)

flag = aes_obj.decrypt(flag_enc)
print flag

得到:AES_1s_b3TteR\0x03\0x03\0x03

除去后面的padding并加上hctf{}得到flag:hctf{AES_1s_b3TteR}

explorer的奇怪番外4

下载:http://pan.baidu.com/s/1eSh1HQE 密码:urap

这次我们继续玩java逆向(审计)。
当然这次的逆向(审计)就没有上次的那么简单了。首先你需要对java的classloader有一个了解才行。
话不多说,jar在此
hint:好好学java

老方法。先扔到jd-gui.exe里。

发现4个class:
0CC175B9C0F1B6A831C399E269772661.class
B51E17DBDB36295E7AB7541157AE7480.class
Main.class
myClass.class

jar里完整的源码我就不放了,看到两段:

1
2
3
4
myClass mc = new myClass();
Class clazz = mc.loadClass("hgame2.checkFlag");
Method c = clazz.getMethod("trueMain", (Class[])null);
c.invoke(null, new Object[0]);
1
2
3
4
5
try {
res = res.substring(0, l + 1) + md5(className.getBytes()) + ".class";
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}

得知,以上加密的两个class的名字是原来名字的md5值,由代码知一个为 checkFlag ,MD5破解得另一
个为 a ,当然这不是重点。

分析myClass知,class中的内容使用AES加密的。

key的生成:

1
2
3
4
5
6
7
8
9
10
11
private static String code = "explorer";
...
MessageDigest md = null;
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
assert (md != null);
md.update(code.getBytes());
byte[] key = md.digest();

iv:String ivStr = "****************";

python解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Cipher import AES
import hashlib
enc1 = open("0CC175B9C0F1B6A831C399E269772661.class",'rb').read()
enc2 = open("B51E17DBDB36295E7AB7541157AE7480.class",'rb').read()
key = hashlib.md5('explorer').digest()
iv = '****************'

mode = AES.MODE_CBC
obj1 = AES.new(key,mode,iv)
obj2 = AES.new(key,mode,iv)
dec1= obj1.decrypt(enc1)
dec2= obj2.decrypt(enc2)

fp = open('dec_0CC175B9C0F1B6A831C399E269772661.class','wb')
fp.write(dec1)
fp.close()

fp = open('dec_B51E17DBDB36295E7AB7541157AE7480.class','wb')
fp.write(dec2)
fp.close()

此处提醒一下,key那里用的是digest(),我之前误把digest()写成了hexdigest(),结果解不出来。
举例:
md5 = hashlib.md5(‘adsf’)
md5.digest() //返回: ‘\x05\xc1*(s48l\x94\x13\x1a\xb8\xaa\x00\xd0\x8a’
md5.hexdigest() //返回: ‘05c12a287334386c94131ab8aa00d08a’

从而我们得到了两个解密后的class,把它们用压缩软件再塞回jar,再次用jd-gui打开jar,反编译出刚才解密的两个class的代码:

dec_0CC175B9C0F1B6A831C399E269772661.class:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package hgame2;

import java.util.Arrays;

public class a
{
public static boolean check(String flag)
{
byte[] var100 = flag.getBytes();

byte[] var72 = new byte[var100.length + 2];
System.arraycopy(var100, 0, var72, 0, var100.length);
byte[] var140 = new byte[var72.length / 3 * 4];
byte[] var82 = var140;
int var17 = 0;
int var18 = 0;

while (var17 < var100.length) {
var82[var18] = (byte)(var72[var17] >>> 2 & 0x3F);
var82[(var18 + 1)] = (byte)(var72[(var17 + 1)] >>> 4 & 0xF | var72[var17] << 4 & 0x3F);
var82[(var18 + 2)] = (byte)(var72[(var17 + 2)] >>> 6 & 0x3 | var72[(var17 + 1)] << 2 & 0x3F);
var82[(var18 + 3)] = (byte)(var72[(var17 + 2)] & 0x3F);
var17 += 3;
var18 += 4;
}
var17 = 0;

while (var17 < var82.length) {
int var10000 = var82[var17];

if (var10000 < 26) {
var82[var17] = (byte)(var82[var17] + 65);
}
else {
var10000 = var82[var17];
byte var10001 = 52;

if (var10000 < var10001) {
var82[var17] = (byte)(var82[var17] + 97 - 26);
}
else {
var10000 = var82[var17];
var10001 = 62;

if (var10000 < var10001) {
var82[var17] = (byte)(var82[var17] + 48 - 52);
}
else {
var10000 = var82[var17];
var10001 = 63;

if (var10000 < var10001) {
var82[var17] = 43;
}
else {
var140 = var82;
int var136 = var17;
var140[var136] = 47;
}
}
}
}
var17++;
}

int var10000 = var82.length;
byte var10001 = 1;
var17 = var10000 - var10001;

while (var17 > var100.length * 4 / 3) {
var82[var17] = 61;
var17--;
}

for (int i = 0; i < var82.length; i++) {
var82[i] = (byte)(var82[i] ^ 0xCC);
}

byte[] f = { -107, -74, -118, -92, -81, -1, -126, -127, -127, -117, -118, -89, -106, -108, -122, -86, -127, -102, -126, -86, -127, -101, -7, -4, -106, -108, -123, -74, -81, -1, -98, -122, -82, -95, -81, -15 };
return Arrays.equals(var82, f);
}
}

dec_B51E17DBDB36295E7AB7541157AE7480.class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package hgame2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;

public class checkFlag
{
public static void trueMain()
throws IOException
{
System.out.print("Now give me flag: ");
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
String flag = strin.readLine();
if (a.check(flag))
System.out.println("hctf{" + flag + "}");
else
System.out.println("try again");
}
}

主要的算法在dec_0CC175B9C0F1B6A831C399E269772661.class中。首先是一个base64,应该能看出来,后边是一个简单的异或。

python解密:

1
2
3
4
5
6
7
8
9
import base64
c=[-107, -74, -118, -92, -81, -1, -126, -127, -127, -117, -118, -89, -106, -108, -122, -86, -127, -102, -126, -86, -127, -101, -7, -4, -106, -108, -123, -74, -81, -1, -98, -122, -82, -95, -81, -15 ]
for i in range(len(c)):
if c[i]<0:
c[i]+=256
c[i] ^= 0xCC
c[i] = chr(c[i])
c = ''.join(c)
print 'hctf{'+base64.b64decode(c)+'}'

flag:hctf{c1assL0ader_1S_1nter3stIng}

explorer的奇怪番外7

下载:http://pan.baidu.com/s/1gf7OdRP 密码:ma6u

这次我们继续玩java手机apk逆向。
所有,到底有没有人会安卓开发呢Orz
话不多说,apk在此
hint:好好学java&安卓代码

工具用的jeb。安卓逆向,首先想到的是找找有没有字符串做参考,来到values\strings.xml,发现字段:

1
2
3
4
<string name="check_flag">
check_flag</string>
<string name="enter_password">
enter password</string>

挺好的,再来到values\public.xml,找到字段:

1
2
3
4
5
6
<public id="0x7f04001a" name="check_flag" type="layout" />
...
<public id="0x7f060015" name="check_flag" type="string" />
...
<public id="0x7f0c0054" name="check_flag" type="id" />

最后通过type为“id”的public id (2131492948)找到了相关的代码:

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
protected void onCreate(Bundle arg3) {
super.onCreate(arg3);
this.setContentView(2130968602);
this.editText = this.findViewById(2131492949);
this.button = this.findViewById(2131492948);
this.textView = this.findViewById(2131492950);
this.button.setOnClickListener(new View$OnClickListener() {
public void onClick(View arg11) {
String v6 = checkFlag.this.editText.getText().toString();
try {
MessageDigest v4 = MessageDigest.getInstance("MD5");
v4.update(v6.getBytes());
if(!Arrays.equals(v4.digest(), new byte[]{-73, 14, 42, 13, -123, 91, 77, -57, -79, -22, 52, -88, -87, -47, 3, 5})) {
return;
}

MessageDigest v7 = MessageDigest.getInstance("sha-256");
v7.update(v6.getBytes());
checkFlag.this.textView.setText("hctf{" + checkFlag.bytes2Hex(v7.digest()) + "}");
}
catch(NoSuchAlgorithmException v0) {
v0.printStackTrace();
}
}
});
}

代码的意思就是获取输入,对输入进行md5加密,加密结果和已知数据比较,如果想等,则对输入进行sha256加密,结果加上‘hctf{}’就是flag了。
得到flag_md5:{-73, 14, 42, 13, -123, 91, 77, -57, -79, -22, 52, -88, -87, -47, 3, 5}
把signed转换成unsigned再hex:

1
2
3
4
5
6
enc = [-73, 14, 42, 13, -123, 91, 77, -57, -79, -22, 52, -88, -87, -47, 3, 5]

for i in range(len(enc)):
enc[i] = chr(enc[i] % 256)

print ''.join(enc).encode('hex')

得到flag_md5:b70e2a0d855b4dc7b1ea34a8a9d10305

md5在线解密:http://www.md5online.org/

结果为:Gabriel

sha256加密得:0c030df5a4e7477d218012c0121ebce6d61bb8dc46e0a6c4f8e1cc8091b946a5

最后flag:hctf{0c030df5a4e7477d218012c0121ebce6d61bb8dc46e0a6c4f8e1cc8091b946a5}