Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

573

574

575

576

577

578

579

580

581

582

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

623

624

625

626

627

628

629

630

631

632

633

634

635

636

637

638

639

640

641

642

643

644

645

646

647

648

649

650

651

652

653

654

655

656

657

658

659

660

661

662

663

664

665

666

667

668

669

670

671

672

673

674

675

676

677

678

679

680

681

682

683

684

685

686

687

688

689

690

691

692

693

694

695

696

697

698

699

700

701

702

703

704

705

706

707

708

709

710

711

712

713

714

715

716

717

718

719

720

721

722

723

724

725

726

727

728

729

730

731

732

733

734

735

736

737

738

739

740

741

742

743

744

745

746

747

748

749

750

751

752

753

754

755

756

757

758

759

760

761

762

763

764

765

766

767

768

769

770

771

772

773

774

775

776

777

778

779

780

781

782

783

784

785

786

787

788

789

790

791

792

793

794

795

796

797

798

799

800

801

802

803

804

805

806

807

808

809

810

811

812

813

814

815

816

817

818

819

820

821

822

823

824

825

826

827

828

829

830

831

832

833

834

835

836

837

838

839

840

841

842

843

844

845

846

847

848

849

850

851

852

853

854

855

856

857

858

859

860

861

862

863

864

865

866

867

868

869

870

871

872

873

874

875

876

877

878

879

880

881

882

883

884

885

886

887

888

889

890

891

892

893

894

895

896

897

898

899

900

901

902

903

904

905

906

907

908

909

910

911

912

913

914

915

916

917

918

919

920

921

922

923

924

925

926

927

928

929

930

931

932

933

934

935

936

937

938

939

940

941

942

943

944

945

946

947

948

949

950

951

952

953

954

955

956

957

958

959

960

961

962

963

964

965

966

967

968

969

970

971

972

973

974

975

976

977

978

979

980

981

982

983

984

985

986

987

988

989

990

991

992

993

994

995

996

997

998

999

1000

1001

1002

1003

1004

1005

1006

1007

1008

1009

1010

1011

1012

1013

1014

1015

1016

1017

1018

1019

1020

1021

1022

1023

1024

1025

1026

1027

1028

1029

1030

1031

1032

1033

1034

1035

1036

1037

1038

1039

1040

1041

1042

1043

1044

1045

1046

1047

1048

1049

1050

1051

1052

1053

1054

1055

1056

1057

1058

1059

1060

1061

1062

1063

1064

1065

1066

1067

1068

1069

1070

1071

1072

1073

1074

1075

1076

1077

1078

1079

1080

1081

1082

1083

1084

1085

1086

1087

1088

1089

1090

1091

1092

1093

1094

1095

1096

1097

1098

1099

1100

1101

1102

1103

1104

1105

1106

1107

1108

1109

1110

1111

1112

1113

1114

1115

1116

1117

1118

1119

1120

1121

1122

1123

1124

1125

1126

1127

1128

1129

1130

1131

1132

1133

1134

1135

1136

1137

1138

1139

1140

1141

1142

1143

1144

1145

1146

1147

1148

1149

1150

1151

1152

1153

1154

1155

1156

1157

1158

1159

1160

1161

1162

1163

1164

1165

1166

1167

1168

1169

1170

1171

1172

1173

1174

1175

1176

1177

1178

1179

1180

1181

1182

1183

1184

1185

1186

1187

1188

1189

1190

1191

1192

1193

1194

1195

1196

1197

1198

1199

1200

1201

1202

1203

1204

1205

1206

1207

1208

1209

1210

1211

1212

1213

1214

1215

1216

1217

1218

1219

1220

1221

1222

1223

1224

1225

1226

1227

1228

1229

1230

1231

1232

1233

1234

1235

1236

1237

1238

1239

1240

1241

1242

1243

1244

1245

1246

1247

1248

1249

1250

1251

1252

1253

1254

1255

1256

1257

1258

1259

1260

1261

1262

1263

1264

1265

1266

1267

1268

1269

1270

1271

1272

1273

1274

1275

1276

1277

1278

1279

1280

1281

1282

1283

1284

1285

1286

1287

1288

1289

1290

1291

1292

1293

1294

1295

1296

1297

1298

1299

1300

1301

1302

1303

1304

1305

1306

1307

1308

1309

1310

1311

1312

1313

1314

1315

1316

1317

1318

1319

1320

1321

1322

1323

1324

1325

1326

1327

1328

1329

1330

1331

1332

1333

1334

1335

1336

1337

1338

1339

1340

1341

1342

1343

1344

1345

1346

1347

1348

1349

1350

1351

1352

1353

1354

1355

1356

1357

1358

1359

1360

1361

1362

1363

1364

1365

1366

1367

1368

1369

1370

1371

1372

1373

1374

1375

1376

1377

1378

1379

1380

1381

1382

1383

1384

1385

1386

1387

1388

1389

1390

1391

1392

1393

1394

1395

1396

1397

1398

1399

1400

1401

1402

1403

1404

1405

1406

1407

1408

1409

1410

1411

1412

1413

1414

1415

1416

1417

1418

1419

1420

1421

1422

1423

1424

1425

1426

1427

1428

1429

1430

1431

1432

1433

1434

1435

1436

1437

1438

1439

1440

1441

1442

1443

1444

1445

1446

1447

1448

1449

1450

1451

1452

1453

1454

1455

1456

1457

1458

1459

1460

1461

1462

1463

1464

1465

1466

1467

1468

1469

1470

1471

1472

1473

1474

1475

1476

1477

1478

1479

1480

1481

1482

1483

1484

1485

1486

1487

1488

1489

1490

1491

1492

1493

1494

1495

1496

1497

1498

1499

1500

1501

1502

1503

1504

1505

1506

1507

1508

1509

1510

1511

1512

1513

1514

1515

1516

1517

1518

1519

1520

1521

1522

1523

1524

1525

1526

1527

1528

1529

1530

1531

1532

1533

1534

1535

1536

1537

1538

1539

1540

1541

1542

1543

1544

1545

1546

1547

1548

1549

1550

1551

1552

1553

1554

1555

1556

1557

1558

1559

1560

1561

1562

1563

1564

1565

1566

1567

1568

1569

1570

1571

1572

1573

1574

1575

1576

1577

1578

1579

1580

1581

1582

1583

1584

1585

1586

1587

1588

1589

1590

1591

1592

1593

1594

1595

1596

1597

1598

1599

1600

1601

1602

1603

1604

1605

1606

1607

1608

1609

1610

1611

1612

1613

1614

1615

1616

1617

1618

1619

1620

1621

1622

1623

1624

1625

1626

1627

1628

1629

1630

1631

1632

1633

1634

1635

1636

1637

1638

1639

1640

1641

1642

1643

1644

1645

1646

1647

1648

1649

1650

1651

1652

1653

1654

1655

1656

1657

1658

1659

1660

1661

1662

1663

1664

1665

1666

1667

1668

1669

1670

1671

1672

1673

1674

1675

1676

1677

1678

1679

1680

1681

1682

1683

1684

1685

1686

1687

1688

1689

1690

1691

1692

1693

1694

1695

1696

1697

1698

1699

1700

1701

1702

1703

1704

# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. 

# 

# This software is provided under under a slightly modified version 

# of the Apache Software License. See the accompanying LICENSE file 

# for more information. 

# 

# Author: Alberto Solino (@agsolino) 

# 

# Description: 

# [MS-SMB2] Protocol Implementation (SMB2 and SMB3) 

# As you might see in the code, it's implemented strictly following  

# the structures defined in the protocol specification. This may 

# not be the most efficient way (e.g. self._Connection is the 

# same to self._Session in the context of this library ) but 

# it certainly helps following the document way easier. 

# 

# ToDo:  

# [X] Implement SMB2_CHANGE_NOTIFY 

# [X] Implement SMB2_QUERY_INFO 

# [X] Implement SMB2_SET_INFO 

# [ ] Implement SMB2_OPLOCK_BREAK 

# [X] Implement SMB3 signing  

# [X] Implement SMB3 encryption 

# [ ] Add more backward compatible commands from the smb.py code 

# [ ] Fix up all the 'ToDo' comments inside the code 

# 

from __future__ import division 

from __future__ import print_function 

 

import socket 

import ntpath 

import random 

import string 

import struct 

from six import indexbytes, b 

from binascii import a2b_hex 

from contextlib import contextmanager 

from pyasn1.type.univ import noValue 

from Cryptodome.Cipher import AES 

 

from impacket import nmb, ntlm, uuid, crypto 

from impacket.smb3structs import * 

from impacket.nt_errors import STATUS_SUCCESS, STATUS_MORE_PROCESSING_REQUIRED, STATUS_INVALID_PARAMETER, \ 

STATUS_NO_MORE_FILES, STATUS_PENDING, STATUS_NOT_IMPLEMENTED, ERROR_MESSAGES 

from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp, ASN1_OID, asn1encode, ASN1_AID 

from impacket.krb5.gssapi import KRB5_AP_REQ 

 

 

# For signing 

import hashlib, hmac, copy 

 

# Our random number generator 

try: 

rand = random.SystemRandom() 

except NotImplementedError: 

rand = random 

pass 

 

# Structs to be used 

TREE_CONNECT = { 

'ShareName' : '', 

'TreeConnectId' : 0, 

'Session' : 0, 

'IsDfsShare' : False, 

# If the client implements the SMB 3.0 dialect,  

# the client MUST also implement the following 

'IsCAShare' : False, 

'EncryptData' : False, 

'IsScaleoutShare' : False, 

# Outside the protocol 

'NumberOfUses' : 0, 

} 

 

FILE = { 

'OpenTable' : [], 

'LeaseKey' : '', 

'LeaseState' : 0, 

'LeaseEpoch' : 0, 

} 

 

OPEN = { 

'FileID' : '', 

'TreeConnect' : 0, 

'Connection' : 0, # Not Used 

'Oplocklevel' : 0, 

'Durable' : False, 

'FileName' : '', 

'ResilientHandle' : False, 

'LastDisconnectTime' : 0, 

'ResilientTimeout' : 0, 

'OperationBuckets' : [], 

# If the client implements the SMB 3.0 dialect,  

# the client MUST implement the following 

'CreateGuid' : '', 

'IsPersistent' : False, 

'DesiredAccess' : '', 

'ShareMode' : 0, 

'CreateOption' : '', 

'FileAttributes' : '', 

'CreateDisposition' : '', 

} 

 

REQUEST = { 

'CancelID' : '', 

'Message' : '', 

'Timestamp' : 0, 

} 

 

CHANNEL = { 

'SigningKey' : '', 

'Connection' : 0, 

} 

 

 

class SessionError(Exception): 

def __init__( self, error = 0, packet=0): 

Exception.__init__(self) 

self.error = error 

self.packet = packet 

 

def get_error_code( self ): 

return self.error 

 

def get_error_packet( self ): 

return self.packet 

 

def __str__( self ): 

return 'SMB SessionError: %s(%s)' % (ERROR_MESSAGES[self.error]) 

 

 

class SMB3: 

def __init__(self, remote_name, remote_host, my_name=None, host_type=nmb.TYPE_SERVER, sess_port=445, timeout=60, 

UDP=0, preferredDialect=None, session=None, negSessionResponse=None): 

 

# [MS-SMB2] Section 3 

self.RequireMessageSigning = False # 

self.ConnectionTable = {} 

self.GlobalFileTable = {} 

self.ClientGuid = ''.join([random.choice(string.ascii_letters) for i in range(16)]) 

# Only for SMB 3.0 

self.EncryptionAlgorithmList = ['AES-CCM'] 

self.MaxDialect = [] 

self.RequireSecureNegotiate = False 

 

# Per Transport Connection Data 

self._Connection = { 

# Indexed by SessionID 

#'SessionTable' : {},  

# Indexed by MessageID 

'OutstandingRequests' : {}, 

'OutstandingResponses' : {}, # 

'SequenceWindow' : 0, # 

'GSSNegotiateToken' : '', # 

'MaxTransactSize' : 0, # 

'MaxReadSize' : 0, # 

'MaxWriteSize' : 0, # 

'ServerGuid' : '', # 

'RequireSigning' : False, # 

'ServerName' : '', # 

# If the client implements the SMB 2.1 or SMB 3.0 dialects, it MUST  

# also implement the following 

'Dialect' : 0, # 

'SupportsFileLeasing' : False, # 

'SupportsMultiCredit' : False, # 

# If the client implements the SMB 3.0 dialect,  

# it MUST also implement the following 

'SupportsDirectoryLeasing' : False, # 

'SupportsMultiChannel' : False, # 

'SupportsPersistentHandles': False, # 

'SupportsEncryption' : False, # 

'ClientCapabilities' : 0, 

'ServerCapabilities' : 0, # 

'ClientSecurityMode' : 0, # 

'ServerSecurityMode' : 0, # 

# Outside the protocol 

'ServerIP' : '', # 

'ClientName' : '', # 

} 

 

self._Session = { 

'SessionID' : 0, # 

'TreeConnectTable' : {}, # 

'SessionKey' : b'', # 

'SigningRequired' : False, # 

'Connection' : 0, #  

'UserCredentials' : '', # 

'OpenTable' : {}, # 

# If the client implements the SMB 3.0 dialect,  

# it MUST also implement the following 

'ChannelList' : [], 

'ChannelSequence' : 0, 

#'EncryptData' : False, 

'EncryptData' : True, 

'EncryptionKey' : '', 

'DecryptionKey' : '', 

'SigningKey' : '', 

'ApplicationKey' : b'', 

# Outside the protocol 

'SessionFlags' : 0, #  

'ServerName' : '', # 

'ServerDomain' : '', # 

'ServerDNSDomainName' : '', # 

'ServerOS' : '', # 

'SigningActivated' : False, # 

} 

 

self.SMB_PACKET = SMB2Packet 

 

self._timeout = timeout 

self._Connection['ServerIP'] = remote_host 

self._NetBIOSSession = None 

self._preferredDialect = preferredDialect 

self._doKerberos = False 

 

self.__userName = '' 

self.__password = '' 

self.__domain = '' 

self.__lmhash = '' 

self.__nthash = '' 

self.__kdc = '' 

self.__aesKey = '' 

self.__TGT = None 

self.__TGS = None 

 

225 ↛ 226line 225 didn't jump to line 226, because the condition on line 225 was never true if sess_port == 445 and remote_name == '*SMBSERVER': 

self._Connection['ServerName'] = remote_host 

else: 

self._Connection['ServerName'] = remote_name 

 

# This is on purpose. I'm still not convinced to do a socket.gethostname() if not specified 

231 ↛ 234line 231 didn't jump to line 234, because the condition on line 231 was never false if my_name is None: 

self._Connection['ClientName'] = '' 

else: 

self._Connection['ClientName'] = my_name 

 

if session is None: 

237 ↛ 244line 237 didn't jump to line 244, because the condition on line 237 was never false if not my_name: 

# If destination port is 139 yes, there's some client disclosure 

my_name = socket.gethostname() 

i = my_name.find('.') 

241 ↛ 244line 241 didn't jump to line 244, because the condition on line 241 was never false if i > -1: 

my_name = my_name[:i] 

 

244 ↛ 245line 244 didn't jump to line 245, because the condition on line 244 was never true if UDP: 

self._NetBIOSSession = nmb.NetBIOSUDPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout) 

else: 

self._NetBIOSSession = nmb.NetBIOSTCPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout) 

 

self.negotiateSession(preferredDialect) 

else: 

self._NetBIOSSession = session 

# We should increase the SequenceWindow since a packet was already received. 

self._Connection['SequenceWindow'] += 1 

# Let's negotiate again if needed (or parse the existing response) using the same connection 

self.negotiateSession(preferredDialect, negSessionResponse) 

 

def printStatus(self): 

print("CONNECTION") 

for i in list(self._Connection.items()): 

print("%-40s : %s" % i) 

print() 

print("SESSION") 

for i in list(self._Session.items()): 

print("%-40s : %s" % i) 

 

def getKerberos(self): 

return self._doKerberos 

 

def getServerName(self): 

return self._Session['ServerName'] 

 

def getClientName(self): 

return self._Session['ClientName'] 

 

def getRemoteName(self): 

276 ↛ 277line 276 didn't jump to line 277, because the condition on line 276 was never true if self._Session['ServerName'] == '': 

return self._Connection['ServerName'] 

return self._Session['ServerName'] 

 

def setRemoteName(self, name): 

self._Session['ServerName'] = name 

return True 

 

def getServerIP(self): 

return self._Connection['ServerIP'] 

 

def getServerDomain(self): 

return self._Session['ServerDomain'] 

 

def getServerDNSDomainName(self): 

return self._Session['ServerDNSDomainName'] 

 

def getServerOS(self): 

return self._Session['ServerOS'] 

 

def getServerOSMajor(self): 

return self._Session['ServerOSMajor'] 

 

def getServerOSMinor(self): 

return self._Session['ServerOSMinor'] 

 

def getServerOSBuild(self): 

return self._Session['ServerOSBuild'] 

 

def isGuestSession(self): 

return self._Session['SessionFlags'] & SMB2_SESSION_FLAG_IS_GUEST 

 

def setTimeout(self, timeout): 

self._timeout = timeout 

 

@contextmanager 

def useTimeout(self, timeout): 

prev_timeout = self.getTimeout(timeout) 

try: 

yield 

finally: 

self.setTimeout(prev_timeout) 

 

def getDialect(self): 

return self._Connection['Dialect'] 

 

def signSMB(self, packet): 

packet['Signature'] = '\x00'*16 

if self._Connection['Dialect'] == SMB2_DIALECT_21 or self._Connection['Dialect'] == SMB2_DIALECT_002: 

325 ↛ exitline 325 didn't return from function 'signSMB', because the condition on line 325 was never false if len(self._Session['SessionKey']) > 0: 

signature = hmac.new(self._Session['SessionKey'], packet.getData(), hashlib.sha256).digest() 

packet['Signature'] = signature[:16] 

else: 

329 ↛ exitline 329 didn't return from function 'signSMB', because the condition on line 329 was never false if len(self._Session['SessionKey']) > 0: 

p = packet.getData() 

signature = crypto.AES_CMAC(self._Session['SigningKey'], p, len(p)) 

packet['Signature'] = signature 

 

def sendSMB(self, packet): 

# The idea here is to receive multiple/single commands and create a compound request, and send it 

# Should return the MessageID for later retrieval. Implement compounded related requests. 

 

# If Connection.Dialect is equal to "3.000" and if Connection.SupportsMultiChannel or 

# Connection.SupportsPersistentHandles is TRUE, the client MUST set ChannelSequence in the 

# SMB2 header to Session.ChannelSequence 

 

# Check this is not a CANCEL request. If so, don't consume sequence numbers 

343 ↛ 346line 343 didn't jump to line 346, because the condition on line 343 was never false if packet['Command'] is not SMB2_CANCEL: 

packet['MessageID'] = self._Connection['SequenceWindow'] 

self._Connection['SequenceWindow'] += 1 

packet['SessionID'] = self._Session['SessionID'] 

 

# Default the credit charge to 1 unless set by the caller 

if ('CreditCharge' in packet.fields) is False: 

packet['CreditCharge'] = 1 

 

# Standard credit request after negotiating protocol 

if self._Connection['SequenceWindow'] > 3: 

packet['CreditRequestResponse'] = 127 

 

messageId = packet['MessageID'] 

 

if self._Session['SigningActivated'] is True and self._Connection['SequenceWindow'] > 2: 

if packet['TreeID'] > 0 and (packet['TreeID'] in self._Session['TreeConnectTable']) is True: 

360 ↛ 367line 360 didn't jump to line 367, because the condition on line 360 was never false if self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is False: 

packet['Flags'] = SMB2_FLAGS_SIGNED 

self.signSMB(packet) 

363 ↛ 367line 363 didn't jump to line 367, because the condition on line 363 was never false elif packet['TreeID'] == 0: 

packet['Flags'] = SMB2_FLAGS_SIGNED 

self.signSMB(packet) 

 

if (self._Session['SessionFlags'] & SMB2_SESSION_FLAG_ENCRYPT_DATA) or ( packet['TreeID'] != 0 and self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is True): 

plainText = packet.getData() 

transformHeader = SMB2_TRANSFORM_HEADER() 

transformHeader['Nonce'] = ''.join([rand.choice(string.ascii_letters) for _ in range(11)]) 

transformHeader['OriginalMessageSize'] = len(plainText) 

transformHeader['EncryptionAlgorithm'] = SMB2_ENCRYPTION_AES128_CCM 

transformHeader['SessionID'] = self._Session['SessionID'] 

cipher = AES.new(self._Session['EncryptionKey'], AES.MODE_CCM, b(transformHeader['Nonce'])) 

cipher.update(transformHeader.getData()[20:]) 

cipherText = cipher.encrypt(plainText) 

transformHeader['Signature'] = cipher.digest() 

packet = transformHeader.getData() + cipherText 

 

self._NetBIOSSession.send_packet(packet) 

else: 

self._NetBIOSSession.send_packet(packet.getData()) 

 

return messageId 

 

def recvSMB(self, packetID = None): 

# First, verify we don't have the packet already 

388 ↛ 389line 388 didn't jump to line 389, because the condition on line 388 was never true if packetID in self._Connection['OutstandingResponses']: 

return self._Connection['OutstandingResponses'].pop(packetID) 

 

data = self._NetBIOSSession.recv_packet(self._timeout) 

 

if data.get_trailer().startswith(b'\xfdSMB'): 

# Packet is encrypted 

transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer()) 

cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11]) 

cipher.update(transformHeader.getData()[20:]) 

plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):]) 

#cipher.verify(transformHeader['Signature']) 

packet = SMB2Packet(plainText) 

else: 

# In all SMB dialects for a response this field is interpreted as the Status field.  

# This field can be set to any value. For a list of valid status codes,  

# see [MS-ERREF] section 2.3. 

packet = SMB2Packet(data.get_trailer()) 

 

# Loop while we receive pending requests 

if packet['Status'] == STATUS_PENDING: 

status = STATUS_PENDING 

while status == STATUS_PENDING: 

data = self._NetBIOSSession.recv_packet(self._timeout) 

412 ↛ 413line 412 didn't jump to line 413, because the condition on line 412 was never true if data.get_trailer().startswith(b'\xfeSMB'): 

packet = SMB2Packet(data.get_trailer()) 

else: 

# Packet is encrypted 

transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer()) 

cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11]) 

cipher.update(transformHeader.getData()[20:]) 

plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):]) 

#cipher.verify(transformHeader['Signature']) 

packet = SMB2Packet(plainText) 

status = packet['Status'] 

 

424 ↛ 433line 424 didn't jump to line 433, because the condition on line 424 was never false if packet['MessageID'] == packetID or packetID is None: 

# Let's update the sequenceWindow based on the CreditsCharged 

# In the SMB 2.0.2 dialect, this field MUST NOT be used and MUST be reserved. 

# The sender MUST set this to 0, and the receiver MUST ignore it. 

# In all other dialects, this field indicates the number of credits that this request consumes. 

if self._Connection['Dialect'] > SMB2_DIALECT_002: 

self._Connection['SequenceWindow'] += (packet['CreditCharge'] - 1) 

return packet 

else: 

self._Connection['OutstandingResponses'][packet['MessageID']] = packet 

return self.recvSMB(packetID) 

 

def negotiateSession(self, preferredDialect = None, negSessionResponse = None): 

# Let's store some data for later use 

self._Connection['ClientSecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED 

439 ↛ 440line 439 didn't jump to line 440, because the condition on line 439 was never true if self.RequireMessageSigning is True: 

self._Connection['ClientSecurityMode'] |= SMB2_NEGOTIATE_SIGNING_REQUIRED 

self._Connection['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION 

currentDialect = SMB2_DIALECT_WILDCARD 

 

# Do we have a negSessionPacket already? 

if negSessionResponse is not None: 

# Yes, let's store the dialect answered back 

negResp = SMB2Negotiate_Response(negSessionResponse['Data']) 

currentDialect = negResp['DialectRevision'] 

 

450 ↛ 472line 450 didn't jump to line 472, because the condition on line 450 was never false if currentDialect == SMB2_DIALECT_WILDCARD: 

# Still don't know the chosen dialect, let's send our options 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_NEGOTIATE 

negSession = SMB2Negotiate() 

 

negSession['SecurityMode'] = self._Connection['ClientSecurityMode'] 

negSession['Capabilities'] = self._Connection['Capabilities'] 

negSession['ClientGuid'] = self.ClientGuid 

if preferredDialect is not None: 

negSession['Dialects'] = [preferredDialect] 

else: 

negSession['Dialects'] = [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30] 

negSession['DialectCount'] = len(negSession['Dialects']) 

packet['Data'] = negSession 

 

packetID = self.sendSMB(packet) 

ans = self.recvSMB(packetID) 

469 ↛ 472line 469 didn't jump to line 472, because the condition on line 469 was never false if ans.isValidAnswer(STATUS_SUCCESS): 

negResp = SMB2Negotiate_Response(ans['Data']) 

 

self._Connection['MaxTransactSize'] = min(0x100000,negResp['MaxTransactSize']) 

self._Connection['MaxReadSize'] = min(0x100000,negResp['MaxReadSize']) 

self._Connection['MaxWriteSize'] = min(0x100000,negResp['MaxWriteSize']) 

self._Connection['ServerGuid'] = negResp['ServerGuid'] 

self._Connection['GSSNegotiateToken'] = negResp['Buffer'] 

self._Connection['Dialect'] = negResp['DialectRevision'] 

478 ↛ 480line 478 didn't jump to line 480, because the condition on line 478 was never false if (negResp['SecurityMode'] & SMB2_NEGOTIATE_SIGNING_REQUIRED) == SMB2_NEGOTIATE_SIGNING_REQUIRED: 

self._Connection['RequireSigning'] = True 

if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LEASING) == SMB2_GLOBAL_CAP_LEASING: 

self._Connection['SupportsFileLeasing'] = True 

if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LARGE_MTU) == SMB2_GLOBAL_CAP_LARGE_MTU: 

self._Connection['SupportsMultiCredit'] = True 

 

if self._Connection['Dialect'] == SMB2_DIALECT_30: 

# Switching to the right packet format 

self.SMB_PACKET = SMB3Packet 

488 ↛ 489line 488 didn't jump to line 489, because the condition on line 488 was never true if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) == SMB2_GLOBAL_CAP_DIRECTORY_LEASING: 

self._Connection['SupportsDirectoryLeasing'] = True 

490 ↛ 491line 490 didn't jump to line 491, because the condition on line 490 was never true if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_MULTI_CHANNEL) == SMB2_GLOBAL_CAP_MULTI_CHANNEL: 

self._Connection['SupportsMultiChannel'] = True 

492 ↛ 493line 492 didn't jump to line 493, because the condition on line 492 was never true if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) == SMB2_GLOBAL_CAP_PERSISTENT_HANDLES: 

self._Connection['SupportsPersistentHandles'] = True 

494 ↛ 497line 494 didn't jump to line 497, because the condition on line 494 was never false if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_ENCRYPTION) == SMB2_GLOBAL_CAP_ENCRYPTION: 

self._Connection['SupportsEncryption'] = True 

 

self._Connection['ServerCapabilities'] = negResp['Capabilities'] 

self._Connection['ServerSecurityMode'] = negResp['SecurityMode'] 

 

def getCredentials(self): 

return ( 

self.__userName, 

self.__password, 

self.__domain, 

self.__lmhash, 

self.__nthash, 

self.__aesKey, 

self.__TGT, 

self.__TGS) 

 

def kerberosLogin(self, user, password, domain = '', lmhash = '', nthash = '', aesKey='', kdcHost = '', TGT=None, TGS=None): 

# If TGT or TGS are specified, they are in the form of: 

# TGS['KDC_REP'] = the response from the server 

# TGS['cipher'] = the cipher used 

# TGS['sessionKey'] = the sessionKey 

# If we have hashes, normalize them 

if lmhash != '' or nthash != '': 

if len(lmhash) % 2: lmhash = '0%s' % lmhash 

if len(nthash) % 2: nthash = '0%s' % nthash 

try: # just in case they were converted already 

lmhash = a2b_hex(lmhash) 

nthash = a2b_hex(nthash) 

except: 

pass 

 

self.__userName = user 

self.__password = password 

self.__domain = domain 

self.__lmhash = lmhash 

self.__nthash = nthash 

self.__kdc = kdcHost 

self.__aesKey = aesKey 

self.__TGT = TGT 

self.__TGS = TGS 

self._doKerberos= True 

 

sessionSetup = SMB2SessionSetup() 

538 ↛ 539line 538 didn't jump to line 539, because the condition on line 538 was never true if self.RequireMessageSigning is True: 

sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED 

else: 

sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED 

 

sessionSetup['Flags'] = 0 

#sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS 

 

# Importing down here so pyasn1 is not required if kerberos is not used. 

from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set 

from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS 

from impacket.krb5 import constants 

from impacket.krb5.types import Principal, KerberosTime, Ticket 

from pyasn1.codec.der import decoder, encoder 

import datetime 

 

# First of all, we need to get a TGT for the user 

userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) 

556 ↛ 560line 556 didn't jump to line 560, because the condition on line 556 was never false if TGT is None: 

557 ↛ 580line 557 didn't jump to line 580, because the condition on line 557 was never false if TGS is None: 

tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost) 

else: 

tgt = TGT['KDC_REP'] 

cipher = TGT['cipher'] 

sessionKey = TGT['sessionKey'] 

 

# Save the ticket 

# If you want, for debugging purposes 

# from impacket.krb5.ccache import CCache 

# ccache = CCache() 

# try: 

# if TGS is None: 

# ccache.fromTGT(tgt, oldSessionKey, sessionKey) 

# else: 

# ccache.fromTGS(TGS['KDC_REP'], TGS['oldSessionKey'], TGS['sessionKey'] ) 

# ccache.saveFile('/tmp/ticket.bin') 

# except Exception, e: 

# print e 

# pass 

 

# Now that we have the TGT, we should ask for a TGS for cifs 

 

580 ↛ 584line 580 didn't jump to line 584, because the condition on line 580 was never false if TGS is None: 

serverName = Principal('cifs/%s' % (self._Connection['ServerName']), type=constants.PrincipalNameType.NT_SRV_INST.value) 

tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey) 

else: 

tgs = TGS['KDC_REP'] 

cipher = TGS['cipher'] 

sessionKey = TGS['sessionKey'] 

 

# Let's build a NegTokenInit with a Kerberos REQ_AP 

 

blob = SPNEGO_NegTokenInit() 

 

# Kerberos 

blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']] 

 

# Let's extract the ticket from the TGS 

tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0] 

ticket = Ticket() 

ticket.from_asn1(tgs['ticket']) 

 

# Now let's build the AP_REQ 

apReq = AP_REQ() 

apReq['pvno'] = 5 

apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) 

 

opts = list() 

apReq['ap-options'] = constants.encodeFlags(opts) 

seq_set(apReq,'ticket', ticket.to_asn1) 

 

authenticator = Authenticator() 

authenticator['authenticator-vno'] = 5 

authenticator['crealm'] = domain 

seq_set(authenticator, 'cname', userName.components_to_asn1) 

now = datetime.datetime.utcnow() 

 

authenticator['cusec'] = now.microsecond 

authenticator['ctime'] = KerberosTime.to_asn1(now) 

 

encodedAuthenticator = encoder.encode(authenticator) 

 

# Key Usage 11 

# AP-REQ Authenticator (includes application authenticator 

# subkey), encrypted with the application session key 

# (Section 5.5.1) 

encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None) 

 

apReq['authenticator'] = noValue 

apReq['authenticator']['etype'] = cipher.enctype 

apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator 

 

blob['MechToken'] = struct.pack('B', ASN1_AID) + asn1encode( struct.pack('B', ASN1_OID) + asn1encode( 

TypesMech['KRB5 - Kerberos 5'] ) + KRB5_AP_REQ + encoder.encode(apReq)) 

 

sessionSetup['SecurityBufferLength'] = len(blob) 

sessionSetup['Buffer'] = blob.getData() 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_SESSION_SETUP 

packet['Data'] = sessionSetup 

 

packetID = self.sendSMB(packet) 

ans = self.recvSMB(packetID) 

642 ↛ 670line 642 didn't jump to line 670, because the condition on line 642 was never false if ans.isValidAnswer(STATUS_SUCCESS): 

self._Session['SessionID'] = ans['SessionID'] 

self._Session['SigningRequired'] = self._Connection['RequireSigning'] 

self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash) 

self._Session['Connection'] = self._NetBIOSSession.get_socket() 

 

self._Session['SessionKey'] = sessionKey.contents[:16] 

if self._Session['SigningRequired'] is True and self._Connection['Dialect'] == SMB2_DIALECT_30: 

self._Session['SigningKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], b"SMB2AESCMAC\x00", b"SmbSign\x00", 128) 

 

# Do not encrypt anonymous connections 

653 ↛ 654line 653 didn't jump to line 654, because the condition on line 653 was never true if user == '' or self.isGuestSession(): 

self._Connection['SupportsEncryption'] = False 

 

# Calculate the key derivations for dialect 3.0 

657 ↛ 659line 657 didn't jump to line 659, because the condition on line 657 was never false if self._Session['SigningRequired'] is True: 

self._Session['SigningActivated'] = True 

if self._Connection['Dialect'] == SMB2_DIALECT_30 and self._Connection['SupportsEncryption'] is True: 

# SMB 3.0. Encryption available. Let's enforce it if we have AES CCM available 

self._Session['SessionFlags'] |= SMB2_SESSION_FLAG_ENCRYPT_DATA 

self._Session['ApplicationKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], b"SMB2APP\x00", b"SmbRpc\x00", 128) 

self._Session['EncryptionKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], b"SMB2AESCCM\x00", b"ServerIn \x00", 128) 

self._Session['DecryptionKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], b"SMB2AESCCM\x00", b"ServerOut\x00", 128) 

 

return True 

else: 

# We clean the stuff we used in case we want to authenticate again 

# within the same connection 

self._Session['UserCredentials'] = '' 

self._Session['Connection'] = 0 

self._Session['SessionID'] = 0 

self._Session['SigningRequired'] = False 

self._Session['SigningKey'] = '' 

self._Session['SessionKey'] = '' 

self._Session['SigningActivated'] = False 

raise Exception('Unsuccessful Login') 

 

 

def login(self, user, password, domain = '', lmhash = '', nthash = ''): 

# If we have hashes, normalize them 

if lmhash != '' or nthash != '': 

if len(lmhash) % 2: lmhash = '0%s' % lmhash 

if len(nthash) % 2: nthash = '0%s' % nthash 

try: # just in case they were converted already 

lmhash = a2b_hex(lmhash) 

nthash = a2b_hex(nthash) 

except: 

pass 

 

self.__userName = user 

self.__password = password 

self.__domain = domain 

self.__lmhash = lmhash 

self.__nthash = nthash 

self.__aesKey = '' 

self.__TGT = None 

self.__TGS = None 

 

sessionSetup = SMB2SessionSetup() 

701 ↛ 702line 701 didn't jump to line 702, because the condition on line 701 was never true if self.RequireMessageSigning is True: 

sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED 

else: 

sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED 

 

sessionSetup['Flags'] = 0 

#sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS 

 

# Let's build a NegTokenInit with the NTLMSSP 

# TODO: In the future we should be able to choose different providers 

 

blob = SPNEGO_NegTokenInit() 

 

# NTLMSSP 

blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']] 

auth = ntlm.getNTLMSSPType1(self._Connection['ClientName'],domain, self._Connection['RequireSigning']) 

blob['MechToken'] = auth.getData() 

 

sessionSetup['SecurityBufferLength'] = len(blob) 

sessionSetup['Buffer'] = blob.getData() 

 

# ToDo: 

# If this authentication is for establishing an alternative channel for an existing Session, as specified 

# in section 3.2.4.1.7, the client MUST also set the following values: 

# The SessionId field in the SMB2 header MUST be set to the Session.SessionId for the new 

# channel being established. 

# The SMB2_SESSION_FLAG_BINDING bit MUST be set in the Flags field. 

# The PreviousSessionId field MUST be set to zero. 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_SESSION_SETUP 

packet['Data'] = sessionSetup 

 

packetID = self.sendSMB(packet) 

ans = self.recvSMB(packetID) 

736 ↛ exitline 736 didn't return from function 'login', because the condition on line 736 was never false if ans.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED): 

self._Session['SessionID'] = ans['SessionID'] 

self._Session['SigningRequired'] = self._Connection['RequireSigning'] 

self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash) 

self._Session['Connection'] = self._NetBIOSSession.get_socket() 

sessionSetupResponse = SMB2SessionSetup_Response(ans['Data']) 

respToken = SPNEGO_NegTokenResp(sessionSetupResponse['Buffer']) 

 

# Let's parse some data and keep it to ourselves in case it is asked 

ntlmChallenge = ntlm.NTLMAuthChallenge(respToken['ResponseToken']) 

746 ↛ 778line 746 didn't jump to line 778, because the condition on line 746 was never false if ntlmChallenge['TargetInfoFields_len'] > 0: 

av_pairs = ntlm.AV_PAIRS(ntlmChallenge['TargetInfoFields'][:ntlmChallenge['TargetInfoFields_len']]) 

748 ↛ 754line 748 didn't jump to line 754, because the condition on line 748 was never false if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None: 

try: 

self._Session['ServerName'] = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le') 

except: 

# For some reason, we couldn't decode Unicode here.. silently discard the operation 

pass 

754 ↛ 761line 754 didn't jump to line 761, because the condition on line 754 was never false if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None: 

try: 

756 ↛ 761line 756 didn't jump to line 761, because the condition on line 756 was never false if self._Session['ServerName'] != av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le'): 

self._Session['ServerDomain'] = av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le') 

except: 

# For some reason, we couldn't decode Unicode here.. silently discard the operation 

pass 

761 ↛ 769line 761 didn't jump to line 769, because the condition on line 761 was never false if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None: 

try: 

self._Session['ServerDNSDomainName'] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode('utf-16le') 

except: 

# For some reason, we couldn't decode Unicode here.. silently discard the operation 

pass 

 

# Parse Version to know the target Operating system name. Not provided elsewhere anymore 

769 ↛ 778line 769 didn't jump to line 778, because the condition on line 769 was never false if 'Version' in ntlmChallenge.fields: 

version = ntlmChallenge['Version'] 

 

772 ↛ 778line 772 didn't jump to line 778, because the condition on line 772 was never false if len(version) >= 4: 

self._Session['ServerOS'] = "Windows %d.%d Build %d" % (indexbytes(version,0), indexbytes(version,1), struct.unpack('<H',version[2:4])[0]) 

self._Session["ServerOSMajor"] = indexbytes(version,0) 

self._Session["ServerOSMinor"] = indexbytes(version,1) 

self._Session["ServerOSBuild"] = struct.unpack('<H',version[2:4])[0] 

 

type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, respToken['ResponseToken'], user, password, domain, lmhash, nthash) 

 

780 ↛ 785line 780 didn't jump to line 785, because the condition on line 780 was never false if exportedSessionKey is not None: 

self._Session['SessionKey'] = exportedSessionKey 

if self._Session['SigningRequired'] is True and self._Connection['Dialect'] == SMB2_DIALECT_30: 

self._Session['SigningKey'] = crypto.KDF_CounterMode(exportedSessionKey, b"SMB2AESCMAC\x00", b"SmbSign\x00", 128) 

 

respToken2 = SPNEGO_NegTokenResp() 

respToken2['ResponseToken'] = type3.getData() 

 

# Reusing the previous structure 

sessionSetup['SecurityBufferLength'] = len(respToken2) 

sessionSetup['Buffer'] = respToken2.getData() 

 

packetID = self.sendSMB(packet) 

packet = self.recvSMB(packetID) 

try: 

795 ↛ exitline 795 didn't return from function 'login', because the condition on line 795 was never false if packet.isValidAnswer(STATUS_SUCCESS): 

sessionSetupResponse = SMB2SessionSetup_Response(packet['Data']) 

self._Session['SessionFlags'] = sessionSetupResponse['SessionFlags'] 

self._Session['SessionID'] = packet['SessionID'] 

 

# Do not encrypt anonymous connections 

if user == '' or self.isGuestSession(): 

self._Connection['SupportsEncryption'] = False 

 

# Calculate the key derivations for dialect 3.0 

805 ↛ 807line 805 didn't jump to line 807, because the condition on line 805 was never false if self._Session['SigningRequired'] is True: 

self._Session['SigningActivated'] = True 

if self._Connection['Dialect'] == SMB2_DIALECT_30 and self._Connection['SupportsEncryption'] is True: 

# SMB 3.0. Encryption available. Let's enforce it if we have AES CCM available 

self._Session['SessionFlags'] |= SMB2_SESSION_FLAG_ENCRYPT_DATA 

self._Session['ApplicationKey'] = crypto.KDF_CounterMode(exportedSessionKey, b"SMB2APP\x00", b"SmbRpc\x00", 128) 

self._Session['EncryptionKey'] = crypto.KDF_CounterMode(exportedSessionKey, b"SMB2AESCCM\x00",b"ServerIn \x00", 128) 

self._Session['DecryptionKey'] = crypto.KDF_CounterMode(exportedSessionKey, b"SMB2AESCCM\x00",b"ServerOut\x00", 128) 

 

return True 

except: 

# We clean the stuff we used in case we want to authenticate again 

# within the same connection 

self._Session['UserCredentials'] = '' 

self._Session['Connection'] = 0 

self._Session['SessionID'] = 0 

self._Session['SigningRequired'] = False 

self._Session['SigningKey'] = '' 

self._Session['SessionKey'] = '' 

self._Session['SigningActivated'] = False 

raise 

 

def connectTree(self, share): 

 

# Just in case this came with the full path (maybe an SMB1 client), let's just leave  

# the sharename, we'll take care of the rest 

 

#print self._Session['TreeConnectTable'] 

share = share.split('\\')[-1] 

if share in self._Session['TreeConnectTable']: 

# Already connected, no need to reconnect 

treeEntry = self._Session['TreeConnectTable'][share] 

treeEntry['NumberOfUses'] += 1 

self._Session['TreeConnectTable'][treeEntry['TreeConnectId']]['NumberOfUses'] += 1 

return treeEntry['TreeConnectId'] 

 

#path = share 

try: 

_, _, _, _, sockaddr = socket.getaddrinfo(self._Connection['ServerIP'], 80, 0, 0, socket.IPPROTO_TCP)[0] 

remoteHost = sockaddr[0] 

except: 

remoteHost = self._Connection['ServerIP'] 

path = '\\\\' + remoteHost + '\\' +share 

 

treeConnect = SMB2TreeConnect() 

treeConnect['Buffer'] = path.encode('utf-16le') 

treeConnect['PathLength'] = len(path)*2 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_TREE_CONNECT 

packet['Data'] = treeConnect 

packetID = self.sendSMB(packet) 

packet = self.recvSMB(packetID) 

858 ↛ exitline 858 didn't return from function 'connectTree', because the condition on line 858 was never false if packet.isValidAnswer(STATUS_SUCCESS): 

treeConnectResponse = SMB2TreeConnect_Response(packet['Data']) 

treeEntry = copy.deepcopy(TREE_CONNECT) 

treeEntry['ShareName'] = share 

treeEntry['TreeConnectId'] = packet['TreeID'] 

treeEntry['Session'] = packet['SessionID'] 

treeEntry['NumberOfUses'] += 1 

865 ↛ 866line 865 didn't jump to line 866, because the condition on line 865 was never true if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_DFS) == SMB2_SHARE_CAP_DFS: 

treeEntry['IsDfsShare'] = True 

867 ↛ 868line 867 didn't jump to line 868, because the condition on line 867 was never true if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) == SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY: 

treeEntry['IsCAShare'] = True 

 

if self._Connection['Dialect'] == SMB2_DIALECT_30: 

871 ↛ 872line 871 didn't jump to line 872, because the condition on line 871 was never true if (self._Connection['SupportsEncryption'] is True) and ((treeConnectResponse['ShareFlags'] & SMB2_SHAREFLAG_ENCRYPT_DATA) == SMB2_SHAREFLAG_ENCRYPT_DATA): 

treeEntry['EncryptData'] = True 

# ToDo: This and what follows 

# If Session.EncryptData is FALSE, the client MUST then generate an encryption key, a 

# decryption key as specified in section 3.1.4.2, by providing the following inputs and store 

# them in Session.EncryptionKey and Session.DecryptionKey: 

877 ↛ 878line 877 didn't jump to line 878, because the condition on line 877 was never true if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_SCALEOUT) == SMB2_SHARE_CAP_SCALEOUT: 

treeEntry['IsScaleoutShare'] = True 

 

self._Session['TreeConnectTable'][packet['TreeID']] = treeEntry 

self._Session['TreeConnectTable'][share] = treeEntry 

 

return packet['TreeID'] 

 

def disconnectTree(self, treeId): 

886 ↛ 887line 886 didn't jump to line 887, because the condition on line 886 was never true if (treeId in self._Session['TreeConnectTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

 

889 ↛ 897line 889 didn't jump to line 897, because the condition on line 889 was never false if treeId in self._Session['TreeConnectTable']: 

# More than 1 use? descrease it and return, if not, send the packet 

if self._Session['TreeConnectTable'][treeId]['NumberOfUses'] > 1: 

treeEntry = self._Session['TreeConnectTable'][treeId] 

treeEntry['NumberOfUses'] -= 1 

self._Session['TreeConnectTable'][treeEntry['ShareName']]['NumberOfUses'] -= 1 

return True 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_TREE_DISCONNECT 

packet['TreeID'] = treeId 

treeDisconnect = SMB2TreeDisconnect() 

packet['Data'] = treeDisconnect 

packetID = self.sendSMB(packet) 

packet = self.recvSMB(packetID) 

904 ↛ exitline 904 didn't return from function 'disconnectTree', because the condition on line 904 was never false if packet.isValidAnswer(STATUS_SUCCESS): 

shareName = self._Session['TreeConnectTable'][treeId]['ShareName'] 

del(self._Session['TreeConnectTable'][shareName]) 

del(self._Session['TreeConnectTable'][treeId]) 

filesIDToBeRemoved = [] 

for fileID in list(self._Session['OpenTable'].keys()): 

if self._Session['OpenTable'][fileID]['TreeConnect'] == treeId: 

filesIDToBeRemoved.append(fileID) 

for fileIDToBeRemoved in filesIDToBeRemoved: 

del(self._Session['OpenTable'][fileIDToBeRemoved]) 

return True 

 

def create(self, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition, fileAttributes, impersonationLevel = SMB2_IL_IMPERSONATION, securityFlags = 0, oplockLevel = SMB2_OPLOCK_LEVEL_NONE, createContexts = None): 

917 ↛ 918line 917 didn't jump to line 918, because the condition on line 917 was never true if (treeId in self._Session['TreeConnectTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

 

fileName = fileName.replace('/', '\\') 

if len(fileName) > 0: 

fileName = ntpath.normpath(fileName) 

if fileName[0] == '\\': 

fileName = fileName[1:] 

 

926 ↛ 927line 926 didn't jump to line 927, because the condition on line 926 was never true if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True: 

pathName = fileName 

else: 

pathName = '\\\\' + self._Connection['ServerName'] + '\\' + fileName 

 

fileEntry = copy.deepcopy(FILE) 

fileEntry['LeaseKey'] = uuid.generate() 

fileEntry['LeaseState'] = SMB2_LEASE_NONE 

self.GlobalFileTable[pathName] = fileEntry 

 

936 ↛ 938line 936 didn't jump to line 938, because the condition on line 936 was never true if self._Connection['Dialect'] == SMB2_DIALECT_30 and self._Connection['SupportsDirectoryLeasing'] is True: 

# Is this file NOT on the root directory? 

if len(fileName.split('\\')) > 2: 

parentDir = ntpath.dirname(pathName) 

if parentDir in self.GlobalFileTable: 

raise Exception("Don't know what to do now! :-o") 

else: 

parentEntry = copy.deepcopy(FILE) 

parentEntry['LeaseKey'] = uuid.generate() 

parentEntry['LeaseState'] = SMB2_LEASE_NONE 

self.GlobalFileTable[parentDir] = parentEntry 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_CREATE 

packet['TreeID'] = treeId 

951 ↛ 952line 951 didn't jump to line 952, because the condition on line 951 was never true if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True: 

packet['Flags'] = SMB2_FLAGS_DFS_OPERATIONS 

 

smb2Create = SMB2Create() 

smb2Create['SecurityFlags'] = 0 

smb2Create['RequestedOplockLevel'] = oplockLevel 

smb2Create['ImpersonationLevel'] = impersonationLevel 

smb2Create['DesiredAccess'] = desiredAccess 

smb2Create['FileAttributes'] = fileAttributes 

smb2Create['ShareAccess'] = shareMode 

smb2Create['CreateDisposition'] = creationDisposition 

smb2Create['CreateOptions'] = creationOptions 

 

smb2Create['NameLength'] = len(fileName)*2 

if fileName != '': 

smb2Create['Buffer'] = fileName.encode('utf-16le') 

else: 

smb2Create['Buffer'] = '\x00' 

 

970 ↛ 971line 970 didn't jump to line 971, because the condition on line 970 was never true if createContexts is not None: 

smb2Create['Buffer'] += createContexts 

smb2Create['CreateContextsOffset'] = len(SMB2Packet()) + SMB2Create.SIZE + smb2Create['NameLength'] 

smb2Create['CreateContextsLength'] = len(createContexts) 

else: 

smb2Create['CreateContextsOffset'] = 0 

smb2Create['CreateContextsLength'] = 0 

 

packet['Data'] = smb2Create 

 

packetID = self.sendSMB(packet) 

ans = self.recvSMB(packetID) 

if ans.isValidAnswer(STATUS_SUCCESS): 

createResponse = SMB2Create_Response(ans['Data']) 

 

openFile = copy.deepcopy(OPEN) 

openFile['FileID'] = createResponse['FileID'] 

openFile['TreeConnect'] = treeId 

openFile['Oplocklevel'] = oplockLevel 

openFile['Durable'] = False 

openFile['ResilientHandle'] = False 

openFile['LastDisconnectTime'] = 0 

openFile['FileName'] = pathName 

 

# ToDo: Complete the OperationBuckets 

if self._Connection['Dialect'] == SMB2_DIALECT_30: 

openFile['DesiredAccess'] = oplockLevel 

openFile['ShareMode'] = oplockLevel 

openFile['CreateOptions'] = oplockLevel 

openFile['FileAttributes'] = oplockLevel 

openFile['CreateDisposition'] = oplockLevel 

 

# ToDo: Process the contexts  

self._Session['OpenTable'][createResponse['FileID'].getData()] = openFile 

 

# The client MUST generate a handle for the Open, and it MUST  

# return success and the generated handle to the calling application. 

# In our case, str(FileID) 

return createResponse['FileID'].getData() 

 

def close(self, treeId, fileId): 

1011 ↛ 1012line 1011 didn't jump to line 1012, because the condition on line 1011 was never true if (treeId in self._Session['TreeConnectTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

1013 ↛ 1014line 1013 didn't jump to line 1014, because the condition on line 1013 was never true if (fileId in self._Session['OpenTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_CLOSE 

packet['TreeID'] = treeId 

 

smbClose = SMB2Close() 

smbClose['Flags'] = 0 

smbClose['FileID'] = fileId 

 

packet['Data'] = smbClose 

 

packetID = self.sendSMB(packet) 

ans = self.recvSMB(packetID) 

 

1029 ↛ exitline 1029 didn't return from function 'close', because the condition on line 1029 was never false if ans.isValidAnswer(STATUS_SUCCESS): 

del(self.GlobalFileTable[self._Session['OpenTable'][fileId]['FileName']]) 

del(self._Session['OpenTable'][fileId]) 

 

# ToDo Remove stuff from GlobalFileTable 

return True 

 

def read(self, treeId, fileId, offset = 0, bytesToRead = 0, waitAnswer = True): 

# IMPORTANT NOTE: As you can see, this was coded as a recursive function 

# Hence, you can exhaust the memory pretty easy ( large bytesToRead ) 

# This function should NOT be used for reading files directly, but another higher 

# level function should be used that will break the read into smaller pieces 

 

1042 ↛ 1043line 1042 didn't jump to line 1043, because the condition on line 1042 was never true if (treeId in self._Session['TreeConnectTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

1044 ↛ 1045line 1044 didn't jump to line 1045, because the condition on line 1044 was never true if (fileId in self._Session['OpenTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_READ 

packet['TreeID'] = treeId 

 

1051 ↛ 1052line 1051 didn't jump to line 1052, because the condition on line 1051 was never true if self._Connection['MaxReadSize'] < bytesToRead: 

maxBytesToRead = self._Connection['MaxReadSize'] 

else: 

maxBytesToRead = bytesToRead 

 

if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True: 

packet['CreditCharge'] = ( 1 + (maxBytesToRead - 1) // 65536) 

else: 

maxBytesToRead = min(65536,bytesToRead) 

 

smbRead = SMB2Read() 

smbRead['Padding'] = 0x50 

smbRead['FileID'] = fileId 

smbRead['Length'] = maxBytesToRead 

smbRead['Offset'] = offset 

packet['Data'] = smbRead 

 

packetID = self.sendSMB(packet) 

ans = self.recvSMB(packetID) 

 

if ans.isValidAnswer(STATUS_SUCCESS): 

readResponse = SMB2Read_Response(ans['Data']) 

retData = readResponse['Buffer'] 

1074 ↛ 1075line 1074 didn't jump to line 1075, because the condition on line 1074 was never true if readResponse['DataRemaining'] > 0: 

retData += self.read(treeId, fileId, offset+len(retData), readResponse['DataRemaining'], waitAnswer) 

return retData 

 

def write(self, treeId, fileId, data, offset = 0, bytesToWrite = 0, waitAnswer = True): 

# IMPORTANT NOTE: As you can see, this was coded as a recursive function 

# Hence, you can exhaust the memory pretty easy ( large bytesToWrite ) 

# This function should NOT be used for writing directly to files, but another higher 

# level function should be used that will break the writes into smaller pieces 

 

1084 ↛ 1085line 1084 didn't jump to line 1085, because the condition on line 1084 was never true if (treeId in self._Session['TreeConnectTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

1086 ↛ 1087line 1086 didn't jump to line 1087, because the condition on line 1086 was never true if (fileId in self._Session['OpenTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_WRITE 

packet['TreeID'] = treeId 

 

1093 ↛ 1094line 1093 didn't jump to line 1094, because the condition on line 1093 was never true if self._Connection['MaxWriteSize'] < bytesToWrite: 

maxBytesToWrite = self._Connection['MaxWriteSize'] 

else: 

maxBytesToWrite = bytesToWrite 

 

if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True: 

packet['CreditCharge'] = ( 1 + (maxBytesToWrite - 1) // 65536) 

else: 

maxBytesToWrite = min(65536,bytesToWrite) 

 

smbWrite = SMB2Write() 

smbWrite['FileID'] = fileId 

smbWrite['Length'] = maxBytesToWrite 

smbWrite['Offset'] = offset 

smbWrite['WriteChannelInfoOffset'] = 0 

smbWrite['Buffer'] = data[:maxBytesToWrite] 

packet['Data'] = smbWrite 

 

packetID = self.sendSMB(packet) 

1112 ↛ 1115line 1112 didn't jump to line 1115, because the condition on line 1112 was never false if waitAnswer is True: 

ans = self.recvSMB(packetID) 

else: 

return maxBytesToWrite 

 

if ans.isValidAnswer(STATUS_SUCCESS): 

writeResponse = SMB2Write_Response(ans['Data']) 

bytesWritten = writeResponse['Count'] 

1120 ↛ 1121line 1120 didn't jump to line 1121, because the condition on line 1120 was never true if bytesWritten < bytesToWrite: 

bytesWritten += self.write(treeId, fileId, data[bytesWritten:], offset+bytesWritten, bytesToWrite-bytesWritten, waitAnswer) 

return bytesWritten 

 

def queryDirectory(self, treeId, fileId, searchString = '*', resumeIndex = 0, informationClass = FILENAMES_INFORMATION, maxBufferSize = None, enumRestart = False, singleEntry = False): 

1125 ↛ 1126line 1125 didn't jump to line 1126, because the condition on line 1125 was never true if (treeId in self._Session['TreeConnectTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

1127 ↛ 1128line 1127 didn't jump to line 1128, because the condition on line 1127 was never true if (fileId in self._Session['OpenTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_QUERY_DIRECTORY 

packet['TreeID'] = treeId 

 

queryDirectory = SMB2QueryDirectory() 

queryDirectory['FileInformationClass'] = informationClass 

1136 ↛ 1137line 1136 didn't jump to line 1137, because the condition on line 1136 was never true if resumeIndex != 0 : 

queryDirectory['Flags'] = SMB2_INDEX_SPECIFIED 

queryDirectory['FileIndex'] = resumeIndex 

queryDirectory['FileID'] = fileId 

1140 ↛ 1141line 1140 didn't jump to line 1141, because the condition on line 1140 was never true if maxBufferSize is None: 

maxBufferSize = self._Connection['MaxReadSize'] 

queryDirectory['OutputBufferLength'] = maxBufferSize 

queryDirectory['FileNameLength'] = len(searchString)*2 

queryDirectory['Buffer'] = searchString.encode('utf-16le') 

 

packet['Data'] = queryDirectory 

 

if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True: 

packet['CreditCharge'] = ( 1 + (maxBufferSize - 1) // 65536) 

 

packetID = self.sendSMB(packet) 

ans = self.recvSMB(packetID) 

if ans.isValidAnswer(STATUS_SUCCESS): 

queryDirectoryResponse = SMB2QueryDirectory_Response(ans['Data']) 

return queryDirectoryResponse['Buffer'] 

 

def echo(self): 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_ECHO 

smbEcho = SMB2Echo() 

packet['Data'] = smbEcho 

packetID = self.sendSMB(packet) 

ans = self.recvSMB(packetID) 

if ans.isValidAnswer(STATUS_SUCCESS): 

return True 

 

def cancel(self, packetID): 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_CANCEL 

packet['MessageID'] = packetID 

 

smbCancel = SMB2Cancel() 

 

packet['Data'] = smbCancel 

self.sendSMB(packet) 

 

def ioctl(self, treeId, fileId = None, ctlCode = -1, flags = 0, inputBlob = '', maxInputResponse = None, maxOutputResponse = None, waitAnswer = 1): 

if (treeId in self._Session['TreeConnectTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

if fileId is None: 

fileId = '\xff'*16 

else: 

if (fileId in self._Session['OpenTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_IOCTL 

packet['TreeID'] = treeId 

 

smbIoctl = SMB2Ioctl() 

smbIoctl['FileID'] = fileId 

smbIoctl['CtlCode'] = ctlCode 

smbIoctl['MaxInputResponse'] = maxInputResponse 

smbIoctl['MaxOutputResponse'] = maxOutputResponse 

smbIoctl['InputCount'] = len(inputBlob) 

if len(inputBlob) == 0: 

smbIoctl['InputOffset'] = 0 

smbIoctl['Buffer'] = '\x00' 

else: 

smbIoctl['Buffer'] = inputBlob 

smbIoctl['OutputOffset'] = 0 

smbIoctl['MaxOutputResponse'] = maxOutputResponse 

smbIoctl['Flags'] = flags 

 

packet['Data'] = smbIoctl 

 

packetID = self.sendSMB(packet) 

 

if waitAnswer == 0: 

return True 

 

ans = self.recvSMB(packetID) 

 

if ans.isValidAnswer(STATUS_SUCCESS): 

smbIoctlResponse = SMB2Ioctl_Response(ans['Data']) 

return smbIoctlResponse['Buffer'] 

 

def flush(self,treeId, fileId): 

if (treeId in self._Session['TreeConnectTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

if (fileId in self._Session['OpenTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_FLUSH 

packet['TreeID'] = treeId 

 

smbFlush = SMB2Flush() 

smbFlush['FileID'] = fileId 

 

packet['Data'] = smbFlush 

 

packetID = self.sendSMB(packet) 

ans = self.recvSMB(packetID) 

 

if ans.isValidAnswer(STATUS_SUCCESS): 

return True 

 

def lock(self, treeId, fileId, locks, lockSequence = 0): 

if (treeId in self._Session['TreeConnectTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

if (fileId in self._Session['OpenTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_LOCK 

packet['TreeID'] = treeId 

 

smbLock = SMB2Lock() 

smbLock['FileID'] = fileId 

smbLock['LockCount'] = len(locks) 

smbLock['LockSequence'] = lockSequence 

smbLock['Locks'] = ''.join(str(x) for x in locks) 

 

packet['Data'] = smbLock 

 

packetID = self.sendSMB(packet) 

ans = self.recvSMB(packetID) 

 

if ans.isValidAnswer(STATUS_SUCCESS): 

smbFlushResponse = SMB2Lock_Response(ans['Data']) 

return True 

 

# ToDo: 

# If Open.ResilientHandle is TRUE or Connection.SupportsMultiChannel is TRUE, the client MUST 

# do the following: 

# The client MUST scan through Open.OperationBuckets and find an element with its Free field 

# set to TRUE. If no such element could be found, an implementation-specific error MUST be 

# returned to the application. 

# Let the zero-based array index of the element chosen above be referred to as BucketIndex, and 

# let BucketNumber = BucketIndex +1. 

# Set Open.OperationBuckets[BucketIndex].Free = FALSE 

# Let the SequenceNumber of the element chosen above be referred to as BucketSequence. 

# The LockSequence field of the SMB2 lock request MUST be set to (BucketNumber<< 4) + 

# BucketSequence. 

# Increment the SequenceNumber of the element chosen above using MOD 16 arithmetic. 

 

def logoff(self): 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_LOGOFF 

 

smbLogoff = SMB2Logoff() 

 

packet['Data'] = smbLogoff 

 

packetID = self.sendSMB(packet) 

ans = self.recvSMB(packetID) 

 

1289 ↛ exitline 1289 didn't return from function 'logoff', because the condition on line 1289 was never false if ans.isValidAnswer(STATUS_SUCCESS): 

# We clean the stuff we used in case we want to authenticate again 

# within the same connection 

self._Session['UserCredentials'] = '' 

self._Session['Connection'] = 0 

self._Session['SessionID'] = 0 

self._Session['SigningRequired'] = False 

self._Session['SigningKey'] = '' 

self._Session['SessionKey'] = '' 

self._Session['SigningActivated'] = False 

return True 

 

def queryInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0, flags = 0 ): 

1302 ↛ 1303line 1302 didn't jump to line 1303, because the condition on line 1302 was never true if (treeId in self._Session['TreeConnectTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

1304 ↛ 1305line 1304 didn't jump to line 1305, because the condition on line 1304 was never true if (fileId in self._Session['OpenTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_QUERY_INFO 

packet['TreeID'] = treeId 

 

queryInfo = SMB2QueryInfo() 

queryInfo['FileID'] = fileId 

queryInfo['InfoType'] = infoType 

queryInfo['FileInfoClass'] = fileInfoClass 

queryInfo['OutputBufferLength'] = 65535 

queryInfo['AdditionalInformation'] = additionalInformation 

1317 ↛ 1321line 1317 didn't jump to line 1321, because the condition on line 1317 was never false if len(inputBlob) == 0: 

queryInfo['InputBufferOffset'] = 0 

queryInfo['Buffer'] = '\x00' 

else: 

queryInfo['InputBufferLength'] = len(inputBlob) 

queryInfo['Buffer'] = inputBlob 

queryInfo['Flags'] = flags 

 

packet['Data'] = queryInfo 

packetID = self.sendSMB(packet) 

ans = self.recvSMB(packetID) 

 

1329 ↛ exitline 1329 didn't return from function 'queryInfo', because the condition on line 1329 was never false if ans.isValidAnswer(STATUS_SUCCESS): 

queryResponse = SMB2QueryInfo_Response(ans['Data']) 

return queryResponse['Buffer'] 

 

def setInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0 ): 

1334 ↛ 1335line 1334 didn't jump to line 1335, because the condition on line 1334 was never true if (treeId in self._Session['TreeConnectTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

1336 ↛ 1337line 1336 didn't jump to line 1337, because the condition on line 1336 was never true if (fileId in self._Session['OpenTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

 

packet = self.SMB_PACKET() 

packet['Command'] = SMB2_SET_INFO 

packet['TreeID'] = treeId 

 

setInfo = SMB2SetInfo() 

setInfo['InfoType'] = infoType 

setInfo['FileInfoClass'] = fileInfoClass 

setInfo['BufferLength'] = len(inputBlob) 

setInfo['AdditionalInformation'] = additionalInformation 

setInfo['FileID'] = fileId 

setInfo['Buffer'] = inputBlob 

 

packet['Data'] = setInfo 

packetID = self.sendSMB(packet) 

ans = self.recvSMB(packetID) 

 

if ans.isValidAnswer(STATUS_SUCCESS): 

return True 

 

def getSessionKey(self): 

if self.getDialect() == SMB2_DIALECT_30: 

return self._Session['ApplicationKey'] 

else: 

return self._Session['SessionKey'] 

 

def setSessionKey(self, key): 

if self.getDialect() == SMB2_DIALECT_30: 

self._Session['ApplicationKey'] = key 

else: 

self._Session['SessionKey'] = key 

 

###################################################################### 

# Higher level functions 

 

def rename(self, shareName, oldPath, newPath): 

oldPath = oldPath.replace('/', '\\') 

oldPath = ntpath.normpath(oldPath) 

1376 ↛ 1379line 1376 didn't jump to line 1379, because the condition on line 1376 was never false if len(oldPath) > 0 and oldPath[0] == '\\': 

oldPath = oldPath[1:] 

 

newPath = newPath.replace('/', '\\') 

newPath = ntpath.normpath(newPath) 

1381 ↛ 1384line 1381 didn't jump to line 1384, because the condition on line 1381 was never false if len(newPath) > 0 and newPath[0] == '\\': 

newPath = newPath[1:] 

 

treeId = self.connectTree(shareName) 

fileId = None 

try: 

fileId = self.create(treeId, oldPath, MAXIMUM_ALLOWED ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, 0x200020, FILE_OPEN, 0) 

renameReq = FILE_RENAME_INFORMATION_TYPE_2() 

renameReq['ReplaceIfExists'] = 1 

renameReq['RootDirectory'] = '\x00'*8 

renameReq['FileNameLength'] = len(newPath)*2 

renameReq['FileName'] = newPath.encode('utf-16le') 

self.setInfo(treeId, fileId, renameReq, infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_RENAME_INFO) 

finally: 

1395 ↛ 1397line 1395 didn't jump to line 1397, because the condition on line 1395 was never false if fileId is not None: 

self.close(treeId, fileId) 

self.disconnectTree(treeId) 

 

return True 

 

def writeFile(self, treeId, fileId, data, offset = 0): 

finished = False 

writeOffset = offset 

1404 ↛ 1411line 1404 didn't jump to line 1411, because the condition on line 1404 was never false while not finished: 

if len(data) == 0: 

break 

writeData = data[:self._Connection['MaxWriteSize']] 

data = data[self._Connection['MaxWriteSize']:] 

written = self.write(treeId, fileId, writeData, writeOffset, len(writeData)) 

writeOffset += written 

return writeOffset - offset 

 

def listPath(self, shareName, path, password = None): 

# ToDo: Handle situations where share is password protected 

path = path.replace('/', '\\') 

path = ntpath.normpath(path) 

1417 ↛ 1418line 1417 didn't jump to line 1418, because the condition on line 1417 was never true if len(path) > 0 and path[0] == '\\': 

path = path[1:] 

 

treeId = self.connectTree(shareName) 

 

fileId = None 

try: 

# ToDo, we're assuming it's a directory, we should check what the file type is 

fileId = self.create(treeId, ntpath.dirname(path), FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_SHARE_READ | 

FILE_SHARE_WRITE | FILE_SHARE_DELETE, 

FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN, 0) 

res = '' 

files = [] 

from impacket import smb 

while True: 

try: 

res = self.queryDirectory(treeId, fileId, ntpath.basename(path), maxBufferSize=65535, 

informationClass=FILE_FULL_DIRECTORY_INFORMATION) 

nextOffset = 1 

while nextOffset != 0: 

fileInfo = smb.SMBFindFileFullDirectoryInfo(smb.SMB.FLAGS2_UNICODE) 

fileInfo.fromString(res) 

files.append(smb.SharedFile(fileInfo['CreationTime'], fileInfo['LastAccessTime'], 

fileInfo['LastChangeTime'], fileInfo['EndOfFile'], 

fileInfo['AllocationSize'], fileInfo['ExtFileAttributes'], 

fileInfo['FileName'].decode('utf-16le'), 

fileInfo['FileName'].decode('utf-16le'))) 

nextOffset = fileInfo['NextEntryOffset'] 

res = res[nextOffset:] 

1446 ↛ 1450line 1446 didn't jump to line 1450 except SessionError as e: 

1447 ↛ 1448line 1447 didn't jump to line 1448, because the condition on line 1447 was never true if (e.get_error_code()) != STATUS_NO_MORE_FILES: 

raise 

break 

except Exception as e: 

print(str(e)) 

raise 

finally: 

1454 ↛ 1456line 1454 didn't jump to line 1456, because the condition on line 1454 was never false if fileId is not None: 

self.close(treeId, fileId) 

1456 ↛ exitline 1456 didn't except from function 'listPath', because the raise on line 1448 wasn't executed or the raise on line 1452 wasn't executed self.disconnectTree(treeId) 

 

return files 

 

def mkdir(self, shareName, pathName, password = None): 

# ToDo: Handle situations where share is password protected 

pathName = pathName.replace('/', '\\') 

pathName = ntpath.normpath(pathName) 

1464 ↛ 1467line 1464 didn't jump to line 1467, because the condition on line 1464 was never false if len(pathName) > 0 and pathName[0] == '\\': 

pathName = pathName[1:] 

 

treeId = self.connectTree(shareName) 

 

fileId = None 

try: 

fileId = self.create(treeId, pathName, GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 

FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_CREATE, 0) 

finally: 

1474 ↛ 1476line 1474 didn't jump to line 1476, because the condition on line 1474 was never false if fileId is not None: 

self.close(treeId, fileId) 

self.disconnectTree(treeId) 

 

return True 

 

def rmdir(self, shareName, pathName, password = None): 

# ToDo: Handle situations where share is password protected 

pathName = pathName.replace('/', '\\') 

pathName = ntpath.normpath(pathName) 

1484 ↛ 1487line 1484 didn't jump to line 1487, because the condition on line 1484 was never false if len(pathName) > 0 and pathName[0] == '\\': 

pathName = pathName[1:] 

 

treeId = self.connectTree(shareName) 

 

fileId = None 

try: 

fileId = self.create(treeId, pathName, desiredAccess=DELETE | FILE_READ_ATTRIBUTES | SYNCHRONIZE, 

shareMode=FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 

creationOptions=FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, 

creationDisposition=FILE_OPEN, fileAttributes=0) 

from impacket import smb 

delete_req = smb.SMBSetFileDispositionInfo() 

delete_req['DeletePending'] = True 

self.setInfo(treeId, fileId, inputBlob=delete_req, fileInfoClass=SMB2_FILE_DISPOSITION_INFO) 

finally: 

1500 ↛ 1502line 1500 didn't jump to line 1502, because the condition on line 1500 was never false if fileId is not None: 

self.close(treeId, fileId) 

self.disconnectTree(treeId) 

 

return True 

 

def remove(self, shareName, pathName, password = None): 

# ToDo: Handle situations where share is password protected 

pathName = pathName.replace('/', '\\') 

pathName = ntpath.normpath(pathName) 

if len(pathName) > 0 and pathName[0] == '\\': 

pathName = pathName[1:] 

 

treeId = self.connectTree(shareName) 

 

fileId = None 

try: 

fileId = self.create(treeId, pathName,DELETE | FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, FILE_OPEN, 0) 

finally: 

1519 ↛ 1521line 1519 didn't jump to line 1521, because the condition on line 1519 was never false if fileId is not None: 

self.close(treeId, fileId) 

self.disconnectTree(treeId) 

 

return True 

 

def retrieveFile(self, shareName, path, callback, mode = FILE_OPEN, offset = 0, password = None, shareAccessMode = FILE_SHARE_READ): 

# ToDo: Handle situations where share is password protected 

path = path.replace('/', '\\') 

path = ntpath.normpath(path) 

if len(path) > 0 and path[0] == '\\': 

path = path[1:] 

 

treeId = self.connectTree(shareName) 

fileId = None 

from impacket import smb 

try: 

fileId = self.create(treeId, path, FILE_READ_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0) 

res = self.queryInfo(treeId, fileId) 

fileInfo = smb.SMBQueryFileStandardInfo(res) 

fileSize = fileInfo['EndOfFile'] 

if (fileSize-offset) < self._Connection['MaxReadSize']: 

# Skip reading 0 bytes files.  

1542 ↛ 1554line 1542 didn't jump to line 1554, because the condition on line 1542 was never false if (fileSize-offset) > 0: 

data = self.read(treeId, fileId, offset, fileSize-offset) 

callback(data) 

else: 

written = 0 

toBeRead = fileSize-offset 

while written < toBeRead: 

data = self.read(treeId, fileId, offset, self._Connection['MaxReadSize']) 

written += len(data) 

offset += len(data) 

callback(data) 

finally: 

1554 ↛ 1556line 1554 didn't jump to line 1556, because the condition on line 1554 was never false if fileId is not None: 

self.close(treeId, fileId) 

self.disconnectTree(treeId) 

 

def storeFile(self, shareName, path, callback, mode = FILE_OVERWRITE_IF, offset = 0, password = None, shareAccessMode = FILE_SHARE_WRITE): 

# ToDo: Handle situations where share is password protected 

path = path.replace('/', '\\') 

path = ntpath.normpath(path) 

1562 ↛ 1565line 1562 didn't jump to line 1565, because the condition on line 1562 was never false if len(path) > 0 and path[0] == '\\': 

path = path[1:] 

 

treeId = self.connectTree(shareName) 

fileId = None 

try: 

fileId = self.create(treeId, path, FILE_WRITE_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0) 

finished = False 

writeOffset = offset 

1571 ↛ 1578line 1571 didn't jump to line 1578, because the condition on line 1571 was never false while not finished: 

data = callback(self._Connection['MaxWriteSize']) 

if len(data) == 0: 

break 

written = self.write(treeId, fileId, data, writeOffset, len(data)) 

writeOffset += written 

finally: 

1578 ↛ 1580line 1578 didn't jump to line 1580, because the condition on line 1578 was never false if fileId is not None: 

self.close(treeId, fileId) 

self.disconnectTree(treeId) 

 

def waitNamedPipe(self, treeId, pipename, timeout = 5): 

pipename = ntpath.basename(pipename) 

if (treeId in self._Session['TreeConnectTable']) is False: 

raise SessionError(STATUS_INVALID_PARAMETER) 

if len(pipename) > 0xffff: 

raise SessionError(STATUS_INVALID_PARAMETER) 

 

pipeWait = FSCTL_PIPE_WAIT_STRUCTURE() 

pipeWait['Timeout'] = timeout*100000 

pipeWait['NameLength'] = len(pipename)*2 

pipeWait['TimeoutSpecified'] = 1 

pipeWait['Name'] = pipename.encode('utf-16le') 

 

return self.ioctl(treeId, None, FSCTL_PIPE_WAIT,flags=SMB2_0_IOCTL_IS_FSCTL, inputBlob=pipeWait, maxInputResponse = 0, maxOutputResponse=0) 

 

def getIOCapabilities(self): 

res = dict() 

 

res['MaxReadSize'] = self._Connection['MaxReadSize'] 

res['MaxWriteSize'] = self._Connection['MaxWriteSize'] 

return res 

 

 

###################################################################### 

# Backward compatibility functions and alias for SMB1 and DCE Transports 

# NOTE: It is strongly recommended not to use these commands 

# when implementing new client calls. 

get_server_name = getServerName 

get_client_name = getClientName 

get_server_domain = getServerDomain 

get_server_dns_domain_name = getServerDNSDomainName 

get_remote_name = getRemoteName 

set_remote_name = setRemoteName 

get_remote_host = getServerIP 

get_server_os = getServerOS 

get_server_os_major = getServerOSMajor 

get_server_os_minor = getServerOSMinor 

get_server_os_build = getServerOSBuild 

tree_connect_andx = connectTree 

tree_connect = connectTree 

connect_tree = connectTree 

disconnect_tree = disconnectTree 

set_timeout = setTimeout 

use_timeout = useTimeout 

stor_file = storeFile 

retr_file = retrieveFile 

list_path = listPath 

 

def close_session(self): 

1631 ↛ exitline 1631 didn't return from function 'close_session', because the condition on line 1631 was never false if self._NetBIOSSession: 

self._NetBIOSSession.close() 

self._NetBIOSSession = None 

 

def doesSupportNTLMv2(self): 

# Always true :P  

return True 

 

def is_login_required(self): 

# Always true :P  

return True 

 

def is_signing_required(self): 

return self._Connection['RequireSigning'] 

 

def nt_create_andx(self, treeId, fileName, smb_packet=None, cmd = None): 

if len(fileName) > 0 and fileName[0] == '\\': 

fileName = fileName[1:] 

 

if cmd is not None: 

from impacket import smb 

ntCreate = smb.SMBCommand(data = cmd.getData()) 

params = smb.SMBNtCreateAndX_Parameters(ntCreate['Parameters']) 

return self.create(treeId, fileName, params['AccessMask'], params['ShareAccess'], 

params['CreateOptions'], params['Disposition'], params['FileAttributes'], 

params['Impersonation'], params['SecurityFlags']) 

 

else: 

return self.create(treeId, fileName, 

FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA | 

FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | READ_CONTROL, 

FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE, FILE_OPEN, 0 ) 

 

def get_socket(self): 

return self._NetBIOSSession.get_socket() 

 

 

def write_andx(self,tid,fid,data, offset = 0, wait_answer=1, write_pipe_mode = False, smb_packet=None): 

# ToDo: Handle the custom smb_packet situation 

return self.write(tid, fid, data, offset, len(data)) 

 

def TransactNamedPipe(self, tid, fid, data, noAnswer = 0, waitAnswer = 1, offset = 0): 

return self.ioctl(tid, fid, FSCTL_PIPE_TRANSCEIVE, SMB2_0_IOCTL_IS_FSCTL, data, maxOutputResponse = 65535, waitAnswer = noAnswer | waitAnswer) 

 

def TransactNamedPipeRecv(self): 

ans = self.recvSMB() 

 

if ans.isValidAnswer(STATUS_SUCCESS): 

smbIoctlResponse = SMB2Ioctl_Response(ans['Data']) 

return smbIoctlResponse['Buffer'] 

 

 

def read_andx(self, tid, fid, offset=0, max_size = None, wait_answer=1, smb_packet=None): 

# ToDo: Handle the custom smb_packet situation 

1685 ↛ 1686line 1685 didn't jump to line 1686, because the condition on line 1685 was never true if max_size is None: 

max_size = self._Connection['MaxReadSize'] 

return self.read(tid, fid, offset, max_size, wait_answer) 

 

def list_shared(self): 

# In the context of SMB2/3, forget about the old LANMAN, throw NOT IMPLEMENTED 

raise SessionError(STATUS_NOT_IMPLEMENTED) 

 

def open_andx(self, tid, fileName, open_mode, desired_access): 

# ToDo Return all the attributes of the file 

if len(fileName) > 0 and fileName[0] == '\\': 

fileName = fileName[1:] 

 

fileId = self.create(tid,fileName,desired_access, open_mode, FILE_NON_DIRECTORY_FILE, open_mode, 0) 

return fileId, 0, 0, 0, 0, 0, 0, 0, 0 

 

def set_session_key(self, signingKey): 

self._Session['SessionKey'] = signingKey 

self._Session['SigningActivated'] = True 

self._Session['SigningRequired'] = True