├── generate_map.py ├── sorting.py ├── generate_hex.py └── README.md /generate_map.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import random 3 | 4 | _map = [3, 4, 5, 7, 8, 9] 5 | 6 | def generate_map(): 7 | e = random.choice(_map) 8 | if e >= 3 and e <= 5: 9 | n = _map[::-1][0:3][_map[0:3].index(e)] 10 | else: 11 | n = _map[0:3][_map[::-1][0:3].index(e)] 12 | return {"Extended": e, "Normal": n} 13 | -------------------------------------------------------------------------------- /sorting.py: -------------------------------------------------------------------------------- 1 | # Usage: python3 sorting.py [CODE] 2 | import re, base64, sys 3 | 4 | def sorting(code): 5 | list = [ord(chr(eval(j))) for j in ['0x'+ i for i in re.findall('..', base64.b64decode(code).hex())]] 6 | ex = [] 7 | no = [] 8 | for i in list: 9 | if i >= 0 and i <= 127: 10 | no.append(i) 11 | elif i >= 128 and i <= 255: 12 | ex.append(i) 13 | print(f"Extended: {' '.join(map(hex, ex))}") 14 | print(f"Normal: {' '.join(map(hex, no))}") 15 | print(f"Extended: {len(ex)}, Normal: {len(no)}") 16 | 17 | sorting(sys.argv[1]) 18 | -------------------------------------------------------------------------------- /generate_hex.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import random 3 | 4 | _map = [3, 4, 5, 7, 8, 9] 5 | 6 | def generate_map(): 7 | e = random.choice(_map) 8 | if e >= 3 and e <= 5: 9 | n = _map[::-1][0:3][_map[0:3].index(e)] 10 | else: 11 | n = _map[0:3][_map[::-1][0:3].index(e)] 12 | return {"Extended": e, "Normal": n} 13 | 14 | def generate(): 15 | c = generate_map() 16 | ex, no = c["Extended"], c["Normal"] 17 | _chars = random.sample(range(128,255), ex) 18 | _chars.extend(random.sample(range(1,126), no)) 19 | random.shuffle(_chars) 20 | return " ".join(list(map(hex ,_chars))) 21 | 22 | print(generate()) 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # discord-theory-I 2 | 3 | ## *PART: I* 4 | 5 | My attempt to reverse the Discord nitro token generation function. 6 | 7 | The Nitro generation tools thing is common in Discord now, but none of the tools actually works, so I decided to take it to the next level, and reverse the actual tokens in hopes of finding a better way of generation. 8 | 9 | ```diff 10 | - NOTE: This is just for research, I will and I hope no one uses it for bad purposes. 11 | ``` 12 | ## Introduction: 13 | 14 | If you are not familiar with Discord, nitro is a kind of membership, you pay to get access and do some cool things on Discord, like get a GIF profile picture or upload large size photos and videos, and in order to get it you must either buy it directly or having someone offer it to you, in the second case it would be something like this: [`https://discord.gift/hNN5SBsnHTPFFh3Z`](https://discord.gift/hNN5SBsnHTPFFh3Z) 15 | 16 | The Discord Gift URL followed by a 16-length code will redirect you to the claim page. 17 | 18 | ## First look: 19 | 20 | At first sight it looks like Base64 encoded, using Burp Suite Decoder we will be able to get this result: 21 | 22 | ```markdown 23 | 00000000 84 d3 79 48 1b 27 1d 33 c5 16 1d d9 -- -- -- -- �ÓyH�'�3Å��Ù 24 | ``` 25 | 26 | After searching for what each byte in a 12-byte string is, I was able to sort each character and see what the code actually consisted of, 4 extended characters and 8 printable/non-printable characters, you can check [`https://www.rapidtables.com/code/text/ascii-table.html`](https://www.rapidtables.com/code/text/ascii-table.html) to know more about those type of characters. 27 | 28 | - Extended: 29 | 30 | ```markdown 31 | 0x84 0xd3 0xc5 0xd9 32 | ``` 33 | 34 | - Printable/Non-Printable: 35 | 36 | ```markdown 37 | 0x79 0x48 0x1b 0x27 0x1d 0x33 0x16 0x1d 38 | ``` 39 | 40 | Doing this over and over again will take a lot of time, so I coded this function that automates the work, feel free to use it: 41 | 42 | ```python 43 | import re, base64 44 | 45 | def sorting(code): 46 | list = [ord(chr(eval(j))) for j in ['0x'+ i for i in re.findall('..', base64.b64decode(code).hex())]] 47 | ex = [] 48 | no = [] 49 | for i in list: 50 | if i >= 0 and i <= 127: 51 | no.append(i) 52 | elif i >= 128 and i <= 255: 53 | ex.append(i) 54 | print(f"Extended: {' '.join(map(hex, ex))}") 55 | print(f"Normal: {' '.join(map(hex, no))}") 56 | print(f"Extended: {len(ex)}, Normal: {len(no)}") 57 | ``` 58 | 59 | ## Finding a Pattern: 60 | 61 | In order to find a pattern, I used the function above to sort different valid codes, and the result I got is: 62 | 63 | ```php 64 | Extended: 0x8e 0xf0 0x8f 0xcb 0xe0 0xba 0xe3 65 | Normal: 0x5f 0x2d 0x59 0x5e 0x4a 66 | Extended: 7, Normal: 5 67 | 68 | Extended: 0xc2 0xeb 0xe1 0xe1 69 | Normal: 0x62 0x75 0x70 0x1c 0x40 0x37 0x77 0x14 70 | Extended: 4, Normal: 8 71 | 72 | Extended: 0xac 0xb0 0x9b 73 | Normal: 0x28 0x72 0x5c 0x30 0x4 0x75 0x72 0x1c 0x6c 74 | Extended: 3, Normal: 9 75 | 76 | Extended: 0xbb 0xa1 0xf9 0x96 0xf5 77 | Normal: 0x71 0x72 0x1d 0x49 0x20 0x1 0x14 78 | Extended: 5, Normal: 7 79 | 80 | Extended: 0xbf 0x96 0xf2 0xb3 0xb0 0x9d 0x8a 81 | Normal: 0x3b 0x4 0x5b 0x4c 0x5c 82 | Extended: 7, Normal: 5 83 | 84 | Extended: 0xd0 0xf1 0x91 0xa9 85 | Normal: 0x65 0x5b 0x17 0x6a 0x1d 0x50 0x70 0x3d 86 | Extended: 4, Normal: 8 87 | ``` 88 | 89 | From this I was able to know a few rules that must be followed in creating the code: 90 | 91 | - Extended characters can be lower or higher than normal (printable / non-printable) characters. 92 | - There are no duplicate characters. 93 | - There is a pattern with 3,4,5,7,8,9. 94 | 95 | Looking at the numbers we can see a pattern, if we choose 3 extended characters from the other side, we'll have a 9 normal characters, it's something like [Caesar Cipher](https://en.wikipedia.org/wiki/Caesar_cipher), and to simplify it: 96 | 97 | ![image](https://user-images.githubusercontent.com/48088579/133827790-e5ff9ede-ad38-4d47-9e0b-9c819b484a2f.png) 98 | 99 | 100 | Putting everything together, we can create a function that generates valid instructions for our code: 101 | 102 | ```python 103 | import random 104 | 105 | _map = [3, 4, 5, 7, 8, 9] 106 | 107 | def generate_map(): 108 | e = random.choice(_map) 109 | if e >= 3 and e <= 5: 110 | n = _map[::-1][0:3][_map[0:3].index(e)] 111 | else: 112 | n = _map[0:3][_map[::-1][0:3].index(e)] 113 | return {"Extended": e, "Normal": n} 114 | ``` 115 | An example: 116 | ```python 117 | PS C:\Users\Desktop\discord-theory> python .\generate_map.py 118 | {'Extended': 5, 'Normal': 7} 119 | PS C:\Users\Desktop\discord-theory> 120 | ``` 121 | Note that I've seen some 24-length nitro codes, but I'm assuming you can just find the right map to generate this type of codes. 122 | 123 | ## Generation: 124 | 125 | In order to create a generation function, by putting everything together according to the rules above, by creating a function that takes the coordinates from `generate_map()` function, a random extended and printable/non-printable characters and shuffle them together and convert them to hex, we will end up with this: 126 | 127 | ```python 128 | import random 129 | 130 | _map = [3, 4, 5, 7, 8, 9] 131 | 132 | def generate_map(): 133 | e = random.choice(_map) 134 | if e >= 3 and e <= 5: 135 | n = _map[::-1][0:3][_map[0:3].index(e)] 136 | else: 137 | n = _map[0:3][_map[::-1][0:3].index(e)] 138 | return {"Extended": e, "Normal": n} 139 | 140 | def generate(): 141 | c = generate_map() 142 | ex, no = c["Extended"], c["Normal"] 143 | _chars = random.sample(range(128,255), ex) 144 | _chars.extend(random.sample(range(1,126), no)) 145 | random.shuffle(_chars) 146 | return " ".join(list(map(hex ,_chars))) 147 | 148 | print(generate()) 149 | ``` 150 | 151 | An example (Hex): 152 | 153 | ``` 154 | 0xd3 0x38 0xe3 0x68 0xd0 0xf6 0xa9 0xfe 0xa7 0xad 0x13 0xb9 155 | ``` 156 | 157 | Base64: 158 | 159 | ``` 160 | 0zjjaND2qf6nrRO5 161 | ``` 162 | 163 | ```php 164 | Extended: 0xd3 0xe3 0xd0 0xf6 0xa9 0xfe 0xa7 0xad 0xb9 165 | Normal: 0x38 0x68 0x13 166 | Extended: 9, Normal: 3 167 | ``` 168 | 169 | # Problems: 170 | 171 | - Nitro code should contain no padding. 172 | - An ethical way to validate the generated codes. 173 | 174 | Thanks for reading <3. 175 | --------------------------------------------------------------------------------