题目说环境没有libc,那就需要一个不需要libc就能执行的可执行文件,用纯汇编写一段orw。
# 将/flag.txt字符串写入栈并将地址保存到r11寄存器
mov r10,0x74;
push r10;
mov r10,0x78742E67616C662f;
push r10;
mov r11,rsp;
# 构造sys_open 打开/flag.txt
sub rsp,0x100;
mov rax,2;
mov rdi,r11;
xor rsi,rsi;
mov rdx,rsi;
syscall;
# 构造sys_read 读取/flag.txt
mov rdi,rax;
mov rsi,rsp;
mov rdx,0x80;
xor rax,rax;
syscall;
# 构造sys_write 输出/flag.txt
mov rdi,1;
mov rax,rdi;
syscall;
生成的shellcode
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 49 C7 C2 74 00 00 00 41 52 49 BA 2F 66 6C 61 67
00000010 2E 74 78 41 52 49 89 E3 48 81 EC 00 01 00 00 48
00000020 C7 C0 02 00 00 00 4C 89 DF 48 31 F6 48 89 F2 0F
00000030 05 48 89 C7 48 89 E6 48 C7 C2 80 00 00 00 48 31
00000040 C0 0F 05 48 C7 C7 01 00 00 00 48 89 F8 0F 05
发现用什么工具编译都不能弄出不依赖libc的东西,静态编译的话又太大。这时想到metasploit编译的meterpreter客户端都短小精悍,一般只有250b,用ida打开发现都是纯纯大汇编。利用winhex将入口(0x78处)的shellcode换成我们的。
覆写完的文件hex
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 7F 45 4C 46 02 01 01 00 00 00 00 00 00 00 00 00 ELF
00000010 02 00 3E 00 01 00 00 00 78 00 40 00 00 00 00 00 > x @
00000020 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 @
00000030 00 00 00 00 40 00 38 00 01 00 00 00 00 00 00 00 @ 8
00000040 01 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00
00000050 00 00 40 00 00 00 00 00 00 00 40 00 00 00 00 00 @ @
00000060 FA 00 00 00 00 00 00 00 7C 01 00 00 00 00 00 00 ? |
00000070 00 10 00 00 00 00 00 00 49 C7 C2 74 00 00 00 41 I锹t A
00000080 52 49 BA 2F 66 6C 61 67 2E 74 78 41 52 49 89 E3 RI?flag.txARI夈
00000090 48 81 EC 00 01 00 00 48 C7 C0 02 00 00 00 4C 89 H ? H抢 L?
000000A0 DF 48 31 F6 48 89 F2 0F 05 48 89 C7 48 89 E6 48 逪1鯤夠 H壡H夋H
000000B0 C7 C2 80 00 00 00 48 31 C0 0F 05 48 C7 C7 01 00 锹€ H1? H乔
000000C0 00 00 48 89 F8 0F 05 25 49 FF C9 74 18 57 6A 23 H夬 %I蓆 Wj#
000000D0 58 6A 00 6A 05 48 89 E7 48 31 F6 0F 05 59 59 5F Xj j H夌H1? YY_
000000E0 48 85 C0 79 C7 6A 3C 58 6A 01 5F 0F 05 5E 6A 7E H吚y莏<Xj _ ^j~
000000F0 5A 0F 05 48 85 C0 78 ED FF E6 Z H吚x??
由于我们直接写入的文件没有可执行权限,chmod命令又不能使用。需要覆写进入一个已有的可执行文件内。利用printf函数无损写入
printf "\x7F\x45\x4C\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3E\x00\x01\x00\x00\x00\x78\x00\x40\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x38\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\xFA\x00\x00\x00\x00\x00\x00\x00\x7C\x01\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x49\xC7\xC2\x74\x00\x00\x00\x41\x52\x49\xBA\x2F\x66\x6C\x61\x67\x2E\x74\x78\x41\x52\x49\x89\xE3\x48\x81\xEC\x00\x01\x00\x00\x48\xC7\xC0\x02\x00\x00\x00\x4C\x89\xDF\x48\x31\xF6\x48\x89\xF2\x0F\x05\x48\x89\xC7\x48\x89\xE6\x48\xC7\xC2\x80\x00\x00\x00\x48\x31\xC0\x0F\x05\x48\xC7\xC7\x01\x00\x00\x00\x48\x89\xF8\x0F\x05\x25\x49\xFF\xC9\x74\x18\x57\x6A\x23\x58\x6A\x00\x6A\x05\x48\x89\xE7\x48\x31\xF6\x0F\x05\x59\x59\x5F\x48\x85\xC0\x79\xC7\x6A\x3C\x58\x6A\x01\x5F\x0F\x05\x5E\x6A\x7E\x5A\x0F\x05\x48\x85\xC0\x78\xED\xFF\xE6" > /usr/bin/base64
执行
root@ret2shell-89-9018:~# base64
moectf{BU5YB0x-i5_SOO0OoO10OOOo0OOoOOOOO0o0OOo_BUSyd}
Segmentation fault (core dumped)
root@ret2shell-89-9018:~#
同上题,先构造纯纯大汇编读取/etc/nginx/nginx.conf
nginx的配置文件
mov r10,0x666E6F632E;
push r10;
mov r10,0x786E69676E2F786E;
push r10;
mov r10,0x69676E2F6374652F;
push r10;
mov r11,rsp;
sub rsp,0x300;
mov rax,2;
mov rdi,r11;
xor rsi,rsi;
mov rdx,rsi;
syscall;
mov rdi,rax;
mov rsi,rsp;
mov rdx,0x800;
xor rax,rax;syscall;
mov rdi,1;
mov rax,rdi;
syscall;
读取到
user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
#
# Basic Settings
#
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
#
# SSL Settings
#
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
#
# Logging Settings
#
access_log /var/log/nginx/access.log;
#
# Gzip Settings
#
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
#
# Virtual Host Configs
#
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
#mail {
# # See sample authentication script at:
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
# # auth_http localhost/auth.php;
# # pop3_capabilities "TOP" "USER";
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
# server {
# listen localhost:110;
# protocol pop3;
# proxy on;
# }
#
# server {
# listen localhost:143;
# protocol imap;
# proxy on;
# }
#}
用cd命令获取各个conf的文件夹,发现仅cd /etc/nginx/sites-enabled/的时候存在default文件,再用纯纯大汇编读取/etc/nginx/sites-enabled/default。
xor r10,r10;
push r10;
mov r10,0x746C75616665642F;
push r10;
mov r10,0x64656C62616E652D;
push r10;
mov r10,0x73657469732F786E;
push r10;
mov r10,0x69676E2F6374652F;
push r10;
mov r11,rsp;
sub rsp,0x300;
mov rax,2;
mov rdi,r11;
xor rsi,rsi;
mov rdx,rsi;
syscall;
mov rdi,rax;
mov rsi,rsp;
mov rdx,0x800;
xor rax,rax;syscall;
mov rdi,1;
mov rax,rdi;
syscall;
读取到
server {
listen 80 default_server;
root /var/www/html;
location / {
index index.html;
}
}
/etc/nginx/sites-enabled/default
现在得知nginx监听的是80端口,用纯纯大汇编写一个监听80端口,并将接收到的tcp流量输出出来,直接写起来有点费劲,想起来meterpreter
的bind_tcp
功能与我期望的功能类似,bind_tcp
是将接收的tcp流量shellcode写入内存跳转执行,我只需要将跳转执行这一段汇编代码改为输出,就能够获取接收的字符串了。
先用msfvenom
生成一个马,
msfvenom -p linux\x64\meterpreter\bind_tcp LPORT=80 -f elf -o nginx
得到的程序反汇编
LOAD:0000000000400078 push 29h ; ')'
LOAD:000000000040007A pop rax
LOAD:000000000040007B cdq ; protocol
LOAD:000000000040007C push 2
LOAD:000000000040007E pop rdi ; family
LOAD:000000000040007F push 1
LOAD:0000000000400081 pop rsi ; type
LOAD:0000000000400082 syscall ; LINUX - sys_socket
LOAD:0000000000400084 xchg rax, rdi ; fd
LOAD:0000000000400086 push rdx
LOAD:0000000000400087 mov [rsp+8+var_8], 50000002h
LOAD:000000000040008E mov rsi, rsp ; backlog
LOAD:0000000000400091 push 10h
LOAD:0000000000400093 pop rdx ; upeer_addrlen
LOAD:0000000000400094 push 31h ; '1'
LOAD:0000000000400096 pop rax
LOAD:0000000000400097 syscall ; LINUX - sys_bind
LOAD:0000000000400099 pop rcx
LOAD:000000000040009A push 32h ; '2'
LOAD:000000000040009C pop rax
LOAD:000000000040009D syscall ; LINUX - sys_listen
LOAD:000000000040009F xchg rax, rsi ; upeer_sockaddr
LOAD:00000000004000A1 push 2Bh ; '+'
LOAD:00000000004000A3 pop rax
LOAD:00000000004000A4 syscall ; LINUX - sys_accept
LOAD:00000000004000A6 push rax
LOAD:00000000004000A7 push rsi
LOAD:00000000004000A8 pop rdi ; addr
LOAD:00000000004000A9 push 9
LOAD:00000000004000AB pop rax
LOAD:00000000004000AC cdq
LOAD:00000000004000AD mov dh, 10h
LOAD:00000000004000AF mov rsi, rdx ; len
LOAD:00000000004000B2 xor r9, r9 ; off
LOAD:00000000004000B5 push 22h ; '"'
LOAD:00000000004000B7 pop r10 ; flags
LOAD:00000000004000B9 mov dl, 7 ; prot
LOAD:00000000004000BB syscall ; LINUX - sys_mmap
LOAD:00000000004000BD xchg rax, rsi
LOAD:00000000004000BF xchg rax, rdi
LOAD:00000000004000C1 pop rdi
LOAD:00000000004000C2 syscall ; LINUX -
LOAD:00000000004000C4 jmp rsi
这里,只需要将末端的jmp rsi
指令替换成以下指令,rsi为转跳地址,就是获取的字符串地址,无需修改,由于汇编码长于之前的代码,需要修改0x60
处的文件大小。
mov rax,1;
mov rdi,rax;
mov rdx,0x800;
syscall;
试一试
root@ret2shell-91-9018:~# printf "\x7F\x45\x4C\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3E\x00\x01\x00\x00\x00\x78\x00\x40\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x38\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\xE6\x00\x00\x00\x00\x00\x00\x00\x14\x01\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x6A\x29\x58\x99\x6A\x02\x5F\x6A\x01\x5E\x0F\x05\x48\x97\x52\xC7\x04\x24\x02\x00\x00\x50\x48\x89\xE6\x6A\x10\x5A\x6A\x31\x58\x0F\x05\x59\x6A\x32\x58\x0F\x05\x48\x96\x6A\x2B\x58\x0F\x05\x50\x56\x5F\x6A\x09\x58\x99\xB6\x10\x48\x89\xD6\x4D\x31\xC9\x6A\x22\x41\x5A\xB2\x07\x0F\x05\x48\x96\x48\x97\x5F\x0F\x05\x48\xC7\xC7\x01\x00\x00\x00\x48\xC7\xC2\x00\x10\x00\x00\x48\x89\xF8\x0F\x05\x48\xC7\xC0\x3C\x00\x00\x00\x48\x31\xFF\x0F\x05" > /usr/bin/base32
root@ret2shell-91-9018:~# base32
GET /files/0b9b96e8-65d1-431f-a056-923c1cded988 HTTP/1.1
accept: */*
host: 127.0.0.1
root@ret2shell-91-9018:~#
多次尝试,发现只有对端发送的代码,没有flag,再次读题,题目说明“客户”:会不断请求损坏的服务器,如果客户高兴的话,就会给你 flag(以请求参数形式传递);
,并且cd 到html目录,发现服务器内存在对应文件,推断需要客户访问到对应的文件内容,才能反馈flag。这个要求代码量极大,用纯纯大汇编写起来非常坐牢,无奈退而求其次,用万能的chatgpt写一段c语言模拟httpserver服务,静态编译完扔回服务器。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PORT 80
#define BUFFER_SIZE 4096
#define WEB_ROOT "/var/www/html"
void handle_client(int client_socket) {
char buffer[BUFFER_SIZE];
char method[16], path[256], protocol[16];
char full_path[BUFFER_SIZE];
int file_fd;
ssize_t bytes_read;
// 读取客户端请求
bytes_read = read(client_socket, buffer, sizeof(buffer) - 1);
if (bytes_read <= 0) {
perror("read");
close(client_socket);
return;
}
buffer[bytes_read] = '\0';
// 将请求内容打印到标准输出
printf("Received request:\n%s\n", buffer);
// 解析 HTTP 请求行(GET /path HTTP/1.1)
sscanf(buffer, "%s %s %s", method, path, protocol);
// 只处理 GET 请求
if (strcmp(method, "GET") != 0) {
const char *response = "HTTP/1.1 501 Not Implemented\r\n\r\n";
write(client_socket, response, strlen(response));
close(client_socket);
return;
}
// 处理请求路径
snprintf(full_path, sizeof(full_path), "%s%s", WEB_ROOT, path);
// 如果请求的是目录,默认返回 index.html
if (full_path[strlen(full_path) - 1] == '/') {
strncat(full_path, "index.html", sizeof(full_path) - strlen(full_path) - 1);
}
// 打开文件
file_fd = open(full_path, O_RDONLY);
if (file_fd < 0) {
// 文件未找到,返回404
const char *response = "HTTP/1.1 404 Not Found\r\n\r\n";
write(client_socket, response, strlen(response));
close(client_socket);
return;
}
// 发送响应头
const char *response_header = "HTTP/1.1 200 OK\r\n\r\n";
write(client_socket, response_header, strlen(response_header));
// 发送文件内容
while ((bytes_read = read(file_fd, buffer, sizeof(buffer))) > 0) {
write(client_socket, buffer, bytes_read);
}
// 关闭文件和客户端连接
close(file_fd);
close(client_socket);
}
int main() {
int server_fd, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);
// 创建套接字
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置服务器地址结构
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
// 绑定套接字到指定的端口
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 开始监听端口
if (listen(server_fd, 10) < 0) {
perror("listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("HTTP server is listening on port %d, serving %s\n", PORT, WEB_ROOT);
// 主循环,处理客户端连接
while (1) {
// 接受客户端连接
client_socket = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_socket < 0) {
perror("accept failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 处理客户端请求
handle_client(client_socket);
}
// 关闭服务器套接字
close(server_fd);
return 0;
}
编译
gcc httpserver.c --static -o httpserver
由于生成的静态文件过大,一次性写入服务器会崩溃。
需要分段写入到可执行文件内,写出python脚本分割文件。
def file_to_hex_chunks(file_path, chunk_size=100000):
i = 1
with open(file_path, 'rb') as file:
while True:
# 读取指定大小的块
chunk = file.read(chunk_size)
if not chunk: # 如果读取不到数据则退出循环
break
# 将每个字符转为\x格式的十六进制
hex_chunk = ''.join(f'\\x{byte:02x}' for byte in chunk)
# 输出结果
f = open(str(i)+".txt","w")
if i == 1:
f.write('printf "'+hex_chunk+'" > /usr/bin/base32')
else:
f.write('printf "'+hex_chunk+'" >> /usr/bin/base32')
f.close()
i +=1
# 示例使用
file_path = './httpserver'
file_to_hex_chunks(file_path)
将分段生成的命令依次输入终端,执行base32命令,得到flag。
root@ret2shell-91-9018:~# base32
HTTP server is listening on port 80, serving /var/www/html
Received request:
GET /files/f4e6aaf0-e319-4f07-941c-a01f4e4622f2 HTTP/1.1
accept: */*
host: 127.0.0.1
Received request:
GET /?flag=moectf{thAT5_not+veRY-H@rD-tO_reSCU3,r1ghT?978492} HTTP/1.1
accept: */*
host: 127.0.0.1
Received request:
GET /files/ca577100-1755-42ab-85d5-8d37b62da6e0 HTTP/1.1
accept: */*
host: 127.0.0.1
Received request:
GET /?flag=moectf{thAT5_not+veRY-H@rD-tO_reSCU3,r1ghT?978492} HTTP/1.1
accept: */*
host: 127.0.0.1
全部评论 (暂无评论)
info 还没有任何评论,你来说两句呐!