0x00: 这个题目是之前和0x9A82学弟一起看的,第一次遇到这种bit位翻转的题目,在讨论了十多分钟之后才搞明白一个国际友人的解法,当时觉得他这个解法太巧妙了~遂做个记录。
0x01: 程序大概的逻辑如下,直接放IDA F5之后的代码好了,部分变量为了阅读方便重命名了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 int __cdecl main (int argc, const char **argv, const char **envp) { signed int v3; __int64 addr; char bit; _BYTE *addr2; void *base_addr; __int64 v8; char buffer; __int64 v11; v11 = *MK_FP(__FS__, 40L L); setbuf(_bss_start, 0L L); puts ("THOU ART GOD, WHITHER CASTEST THY COSMIC RAY?" ); v3 = 1 ; if ( fgets(&buffer, 50 , stdin ) ) { addr = strtol(&buffer, 0L L, 0 ); bit = addr; addr2 = (_BYTE *)(addr >> 3 ); base_addr = (void *)((addr >> 3 ) & 0xFFFFFFFFFFFFF000 LL); if ( mprotect(base_addr, 0x1000 uLL, 7 ) ) { perror("mprotect1" ); } else { v3 = 1 ; *addr2 ^= 1 << (bit & 7 ); if ( mprotect(base_addr, 0x1000 uLL, 5 ) ) { perror("mprotect2" ); } else { puts ("WAS IT WORTH IT???" ); v3 = 0 ; } } } v8 = *MK_FP(__FS__, 40L L); if ( *MK_FP(__FS__, 40L L) == v11 ) LODWORD(v8) = v3; return v8; }
程序逻辑很简单,大概做了这几件事:
读取用户输入,使用strtol()
把输入转成long类型的值。
把这个值右移3 bits,我标记成了addr_base。
调用mprotect()
,修改base_addr指向的内存属性。
addr2最低3 bits反转。
再次调用mprotect()
,修改base_addr指向的内存属性。
0x02:Thinking 开始我的想法是写sc进去然后修改程序流程,跳进去执行。但是并没有搞定,在看了一份wp之后豁然开朗。 这里是main执行结束后返回的汇编代码,老外那份wp里的做法就是,使用bit翻转,把add rsp,48h
编程add rbp,48h
,这样的话,相当于我们可以控制返回地址。 有了上面的基础之后,第一次修改指令,后面构造循环1字节1字节的把shellcode写到合适的位置,然后在最后一次直接retn到那个位置,执行代码。
1 2 3 4 5 6 .text:0000000000400860 add rsp, 48h .text:0000000000400864 pop rbx .text:0000000000400865 pop r14 .text:0000000000400867 pop r15 .text:0000000000400869 pop rbp .text:000000000040086A retn
0x03:Exploit 0x04:Reference