crmaxx / armitage

Automatically exported from code.google.com/p/armitage
0 stars 1 forks source link

Cortana - Simple wait loops break event processing #137

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
Open a windows meterpreter session (reverse_tcp or reverse_http). Then run the 
code below. (Change the "21" in the runModule call to the session you just 
opened)

global('$finished');

local('$i');
for ($i = 0; $i < 10; $i++) {
  runModule(21, "multi/gather/filezilla_client_cred");
}

on ready {
  println("Ready.");
}

on heartbeat_1s {
  println("heartbeat.");
}

sub runModule {
  local('$console $module_name $session_id');
  $session_id = $1;
  $module_name = $2;

  $finished = 0;
  println("Running $module_name");
  $console = console();
  cmd($console, "use $module_name");
  cmd_set($console, %(session => $session_id));
  cmd($console, "run");

  while ($finished == 0) {
    sleep(100);
  }
}

on console { 
  local('$temp');
  foreach $temp (split("\n", [$3 trim])) { 
    #println ($temp);
    if ("[*] Post module execution completed" isin $temp) {
      println("Finished!");
      $finished = 1
    }
  } #foreach
} # on console

What is the expected output? What do you see instead?

I'm expecting:

heartbeat
heartbeat
Ready.
Running multi/gather/filezilla_client_cred
heartbeat
heartbeat
heartbeat
heartbeat
Finished!
Running multi/gather/filezilla_client_cred
heartbeat 
heartbeat
heartbeat
Finished!
...
(for a total of 10 iterations)

Instead, I'm getting:

Running multi/gather/filezilla_client_cred

And then nothing. The heartbeat event no longer processes, and the console 
event never fires. Therefore it just keeps spinning forever. 

Am I missing something here? I've tried all sorts of spawning and forking to 
get this to work, and it's just not happening. If I put the loop in a fork, for 
instance, the $finished variable is out of scope. 

Now, I've been able to implement a really complicated flow control using 
events, but instead of a 3-line for loop, it's a state machine that is executed 
every on heartbeat_1s. This isn't ideal, because once you remove the for loop 
and try running a bunch of *different* modules, the state machine gets an order 
of magnitude more complex.

I don't see any kind of DoEvents method in the Cortana tutorial. That would be 
the ideal solution (instead of sleeping) to fix the problem. (Well, my ideal 
solution at least :)

Charles.

Original issue reported on code.google.com by ChopperC...@gmail.com on 14 May 2013 at 7:40

GoogleCodeExporter commented 9 years ago
Charles,
Cortana does not like busy loops. Everything blocks until the currently 
executing function finishes. This was a deliberate decision made when 
developing Sleep. There is a way to fire an event though..

use fire_event to fire an event globally to all script instances. Use 
fire_event_local to fire an event to the local script instance.

Use fork to create an isolated interpreter environment that doesn't block the 
parent's environment.  fire_event_local will communicate with the parent 
Cortana instance.

Look at lines 32-40 of irc-client.cna:

https://github.com/rsmudge/cortana-scripts/blob/master/irc-client/irc-client.cna

Original comment by rsmu...@gmail.com on 14 May 2013 at 7:49

GoogleCodeExporter commented 9 years ago
The IRC example isn't a good one -- the IRC client isn't waiting on a result 
before the next message can process, it's an asynchronous protocol. I need a 
synchronous loop, where one executes after the other.

I've tried to use fire_event_local, but that's for communicating from the 
script back to the parent. I need a way to get a value from the parent 
($finished) and make it accessible to the child. Pass-by-reference or otherwise.

Original comment by ChopperC...@gmail.com on 14 May 2013 at 8:50

GoogleCodeExporter commented 9 years ago
You can't do a busy loop in Cortana like that, it will block everything else. 
The sleep function blocks as well. There are better ways to do what you want, 
but I don't understand this example. You're trying to run one module 10 times 
and see when any of those 10 runs complete. Is that correct? 

Would you like the modules to run asynchronous of eachother and keep track of 
state on a per module basis, or are you looking to have them run one at a time, 
in a synchronous way?

If you want synchronous, then what I'd do is create a global $console and use 
cmd, cmd_set, and friends. The &cmd functions will queue on a per-console 
basis. So it won't return until the previous command finishes. That's one 
option. There is a 10s timeout built into the &cmd functions.

src/armitage/ConsoleQueue.java is what cmd, cmd_set, and others expose to you.

If you want asynchronous, use &spawn to isolate each set of events to their own 
cortana instance. The &spawn function is the way to decompose complex Cortana 
programs and state flows into something simple and manageable.

Original comment by rsmu...@gmail.com on 14 May 2013 at 9:05

GoogleCodeExporter commented 9 years ago
You can do pass by reference with fork, if it's *really* what you want to do 
(but I don't think it is). The way to do this is:

%store['flag'] = 0;
$lock = semaphore();

fork({
    sleep(10000);
    acquire($lock);
    %store['flag'] = 1;
    release($lock);
}, \%store, \$lock);

while (1) {
    acquire($lock);
    if (%store['flag'] == 1) {
        println("Job is done!");
    }
    release($lock);
}

Original comment by rsmu...@gmail.com on 14 May 2013 at 9:09

GoogleCodeExporter commented 9 years ago
Why would this not be what I want to do? I don't want all of the scripts to run 
at once, I want them to run one after the other. 

I was almost there though, the hash was the key, as it's pass-by-reference. 
This is ultimately what is working for me:

global('%status');

on ready {
  println("Ready.");
  %status['finished'] = 0;
  fork ({    
    local('$i');
    for ($i = 0; $i < 10; $i++) {
      runModule(21, "multi/gather/filezilla_client_cred");
    }
    quit();
  }, \%status);

}

on heartbeat_1s {
  println("heartbeat.");
}

sub runModule {
  %status['finished'] = 0;
  local('$console $module_name $session_id');
  $session_id = $1;
  $module_name = $2;

  println("Running $module_name");
  $console = console();
  cmd($console, "use $module_name");
  cmd_set($console, %(session => $session_id));
  cmd($console, "run");

  while (%status['finished'] == 0) { sleep(100); }
}

on console { 
  local('$temp $r');
  foreach $temp (split("\n", [$3 trim])) { 
    #println ($temp);
    if ("[*] Post module execution completed" isin $temp) {       
      println("Finished!");
      %status['finished'] = 1;
    }
  } #foreach
} # on console

Original comment by ChopperC...@gmail.com on 14 May 2013 at 9:19